Thread的状态
在Thread类中枚举State定义了线程的6种状态
public enum State {
//创建线程实例
NEW,
//执行线程任务
RUNNABLE,
//等待监听锁
BLOCKED,
//调用了wait(),等待别的线程唤醒
WAITING,
//调用了wait(long)
TIMED_WAITING,
//线程任务执行完成
TERMINATED;
}
线程池
Executors中提供了几种创建线程池的API
newSingleThreadExecutor:单线程的线程池
newFixedThreadPool:指定数量的定长线程池
newCachedThreadPool:最大线程数量为Integer.MAX_VALUE的线程池
newScheduledThreadPool:可定时执行任务的线程池,最大线程数量为Integer.MAX_VALUE
这几种方式创建的线程池的背后基本都是创建ThreadPoolExecutor的对象实例,需要注意的是这些线程池用于存放
线程任务的队列可近似认为是无限大的(队列长度为Integer.MAX_VALUE)。如果线程任务过多会导致内存溢出,所以
最好是自己来创建ThreadPoolExecutor实例来定义线程池。
volatile
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去主存中读
取新值,这就保证了可见性。volatile不能保证原子性,如i++这样的操作都不是原子操作,因为他们都进行了多次原子操作,
多个线程同时获取到i的值。
ThreadLocal
每个线程持有变量的一份副本,各个线程操作的是本地内存中的变量。
ThreadLocal的set方法中,获取当前线程的ThreadLocalMap对象,然后将变量设置到当前线程的ThreadLocalMap对象中。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
synchronized
在jdk1.5之前,对并发安全问题可以通过synchronized关键字对代码块进行加锁,但这是一种重量级锁,性能效率比较差。
在这之后,jdk对其进行了优化,根据多线程争夺锁的情况产生不同的状态锁。
自旋锁:当前线程竞争锁失败时,不直接阻塞自己,而是自旋一会,在自旋的同时重新竞争锁,如果自旋结束前获得了锁,
那么锁获取成功;否则,自旋结束后阻塞自己。
偏向锁:如果不仅仅没有实际竞争,自始至终,使用锁的线程都只有一个。轻量级锁每次申请、释放锁都至少需要一次CAS,
但偏向锁只有初始化时需要一次CAS。只需要在Mark Word中CAS记录owner(本质上也是更新,但初始值为空),如果记录
成功,则偏向锁获取成功,记录锁状态为偏向锁,以后当前线程等于owner就可以零成本的直接获得锁;否则,说明有其他线
程竞争,膨胀为轻量级锁。
轻量级锁:假设没有实际的锁竞争。使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程
栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则膨胀为重量级锁。
Lock
jdk1.5之后提供了一种显式锁,通过api的方式去对系统资源加锁和释放锁,对锁的控制更加细化,而且还提供了读写锁,支持
并发下读状态的资源共享。
ReentrantLock中的加锁操作实际调用的是Sync的lock方法,Sync有公平锁和非公平锁,无参的构造方法是非公平锁的实现
NonfairSync.lock()方法中else分支调用AbstractQueuedSynchronizer的acquire方法,我直接把那部分代码放过来了
public void lock() {
sync.lock();
}
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
//通过底层的CAS操作,成功则设置当前线程获得独占锁
setExclusiveOwnerThread(Thread.currentThread());
else {
/**
tryAcquire方法,尝试获取独占锁,为true返回
addWaiter,创建一个线程节点node,添加到虚拟队列中(不存在实例,只有节点关联)
acquireQueued,for (;;)死循环,直到node节点的线程获取到锁
假如acquireQueued返回true,代表线程被中断,执行selfInterrupt
*/
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
线程通信工具类
CountDownLatch:可以把它看作一个计数器,只不过这个计数器的操作是原子操作,当count的值为零时当前线程继续运行
CyclicBarrier:栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生, 如开启3个线程等到某一时刻再同时去执行操作
Semaphore:synchronized 的加强版,作用是控制线程的并发数量。就这一点而言,单纯的synchronized 关键字是实现不了的。