Thread的几种状态
定义在 java.lang.Thread.State 里面 状态分别是:
NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING 、TERMINATED
BLOCKED 和 WAITING的区别
堵塞状态:
一个线程因为等待临界区的锁被阻塞产生的状态
Lock 或者synchronize 关键字产生的状态
等待状态:
一个线程进入了锁,但是需要等待其他线程执行某些操作。时间确定
通过sleep或wait timeout方法进入的限期等待的状态)
同步锁
参考:
一:Monitor
Monitor是一个同步工具,相当于操作系统中的互斥量(mutex),即值为1的信号量。
它内置与每一个Object对象中,相当于一个许可证。拿到许可证即可以进行操作,没有拿到则需要阻塞等待。
二:syncrhoized实现原理
syncrhoized又叫做内置锁,为什么呢?因为使用syncrhoized加锁的同步代码块在字节码引擎中执行时,其实是通过锁对象的monitor的取用与释放来实现的。由上面我们直到Monitor是内置于任何一个对象中的,syncrhoized利用monitor来实现加锁解锁,故syncrhoized又叫做内置锁。
现在我们知道为什么用syncrhoized(lock)来加锁时,锁对象可以是任意对象了:
1:syncrhoized(lock)加锁时,用到的其实只是lock对象内置的monitor而已;
2:一个对象的monitor是唯一的,相当于一个唯一的许可证。拿到许可证的线程才可以执行,执行完后释放对象的monitor才可以被其他线程获取。
我们来讲解一下syncrhoized加锁的同步块的执行过程:
现在假设有代码块: syncrhoized(Object lock){
同步代码...;
}
它在字节码文件中被编译为:monitorenter;//获取monitor许可证,进入同步块
同步代码...
monitorexit;//离开同步块后,释放monitor许可证
条件对象
条件对象是用在这种场景下,比如一个条件获得锁,可是它执行的条件不满足
需要等待,此时可以使用条件对象。
条件对象主要有三个方法
await、notify、notifyAll
最好使用notifyAll,因为notify唤醒的是随机的,如果线程不满足,就继续等待啦,如果没有其他线程唤醒,就死锁啦。
nofigy效率高,nogifyAll效率低。
使用可以参考https://www.jianshu.com/p/25e243850bd2?appinstall=0
账户1余额:200
账户2余额:300
线程1:账户1→账户2(300)
线程2:账户2→账户1(400)
因为线程1和线程2的金额都不足以进行转账,所以两个线程都阻塞了,这种状态就叫死锁(deadlock),如果所有线程死锁,程序就卡死了。
线程可见
使用volatile和final
安全的线程集合
ConcurrentHashMap、CopyOnWriteArrayList
线程池
所有实现了ExecutorService接口(Executor的子接口)的实现类都是线程池,可以分为三大类
ForkJoinPool
ScheduledThreadPoolExecutor
ThreadPoolExecutor
具体的线程池,在工具类Executors中预创建了六小类
实现了ThreadPoolExecutor类:
ExecutorService newCachedThreadPool():无界线程池
ExecutorService newFixedThreadPool():有界线程池
ExecutorService newSingleThreadExecutor():单一线程池
实现了ScheduledThreadPoolExecutor类:
ScheduledExecutorService newSingleThreadScheduledExecutor()
ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
实现了ForkJoinPool类:
ExecutorService newWorkStealingPool()
参数
int corePoolSize:核心线程数,即使空闲也仍保留在池中的线程数
int maximumPoolSize:最大线程数
BlockingQueue workQueue:阻塞队列,在执行任务之前用于保存任务的队列
long keepAliveTime:保持激活时间,当线程数大于核心数时,这是多余的空闲线程在终止之前等待新任务的最大时间