序号 |
类 |
备注 |
核心代码 |
适用场景 |
1 |
synchronized |
同步锁 |
并发锁加在方法级别上,如果是单例class对象,则只能允许一个线程进入 public synchronized void doXXX(){ … } 加在对象上,则与此对象相同的对象都不允许进入该锁的代码块: synchronized(this){ … }
|
排斥相同锁对象进入并发代码块 |
2 |
Semaphore |
信号量锁 |
final Semaphore semaphore = new Semaphore(2,true); //示例new一个对象,指定2个信号量,表示可以允许同时有2个线程进入信号量锁代码块。
semaphore.acquire(); //获取锁,获取不到锁则阻塞。由构造函数的数字决定允许有几个线程可以并发获得这个锁。也就是允许几个线程可以进入这个并发锁持有的代码块。 semaphore.release(); //释放锁,放别的线程进入信号量锁代码块。
|
控制线程并发进入某代码块数量 |
3 |
CountDownLatch |
共享锁 |
final CountDownLatch countDownLatch = new CountDownLatch(1); //这里的数字代表需要扣减几次才能执行锁内代码块
countDownLatch.await(); //线程堵塞,等待执行controller.countDown();代码 countDownLatch.countDown(); //每执行一次,则new此对象时的构造函数给的数字减去1,直到减去结果等于0,执行countDownLatch.await();之后原本阻塞的代码块,相当于signal()唤醒操作。
|
在执行过程中,需要其他已知数量的N个数据作为解除当前线程阻塞的条件。 |
4 |
Exchanger |
交换器 |
创建Exchanger交换器对象,用来同步producer生产者和consumer消费者
Exchanger<List<String>> exchanger = new Exchanger<List<String>>(); List<String> producerList = new ArrayList<String>();//创建生产者 List<String> consumerList = new ArrayList<String>();//创建消费者 生产者集合里放入数据后,数据交换给exchanger交换器,生产者集合回归为空 producerList = exchanger.exchange(producerList); 消费者集合从exchanger交换器中实时获取数据,进行处理。 consumerList = exchanger.exchange(consumerList);
|
生产者和消费者可以控制到一定数量后使用交换器进行交换。 如果不控制数量,始终只允许一个数据进行生产进而消费,可以考虑使用SynchronousQueue
|
5 |
CyclicBarrier |
栅栏锁 |
定义一个最后执行的线程类:
class DoFinalThread implements Runnable { public void run() { … } } DoFinalThread dfThread = new DoFinalThread(); 创建栅栏锁barrier。此对象会等待5个线程。当5个线程结束后,它会执行dfThread线程对象。 CyclicBarrier barrier=new CyclicBarrier(5,dfThread); barrier.await(); //每执行一次,barrier对象计数器从5里面-1,直到减到值为0,就可以执行dfThread线程对象 注意:当dfThread线程对象执行完毕之后,又会去执行那5个线程里barrier.await();后面的代码,因为之前被阻塞了。
|
在同一个点同步任务。
在执行过程中,需要其他已知数量的N个线程执行完毕,最后再执行指定的一个线程。
|
6 |
locks.LockSupport |
阻塞锁 |
LockSupport.park(); //使当前线程安全阻塞
LockSupport.unpark(threadA); //解锁:唤醒阻塞的指定线程
|
当需要额外线程中的数据参与执行当前线程时。 当心死锁:当额外线程的解锁语句执行早于当前线程的阻塞语句时,会发生死锁。
|
7 |
Phaser |
阶段同步锁 |
Phaser phaser = new Phaser(3); //new一个阶段锁,表示将会有3个线程并发参与这些分阶段执行的任务。 phaser.arriveAndAwaitAdvance();// 通知phaser对象当前线程已经完成了当前阶段,需要被阻塞直到其他线程也都完成当前阶段,才继续执行后面代码。小技巧:可以放在每个线程开始的时候执行这个代码,保证不会在所有线程尚未创建完毕时就有某些线程已经开始执行。 phaser.arriveAndDeregister(); //通知phaser对象当前线程已经结束了这个阶段,并且将不再参与接下来的阶段操作。Phaser中唯一一种减少线程参与者数量的方法。 phaser.isTerminated(); //阶段锁是否已终止,返回true: 不再有任何线程在执行任何阶段,所有线程都已经执行完毕phaser.arriveAndDeregister(); phaser.register(); //将一个新的参与者线程注册到Phaser中对象,当phaser对象被某一线程持有引用对象后,就可以调用这个注册方法把该线程注册进来当前阶段。 onAdvance(); //重写Phaser父类方法,如果需要在切换阶段时执行指定事件的话。
|
适用于并发任务,且需要将任务分解成多个步骤分阶段同步完成业务。 |
8 |
ReentrantLock |
lock同步锁 |
ReentrantLock lock = new ReentrantLock(true); //true表示先到的线程先获得锁,锁获得的公平机制 queueLock.lock(); //只允许一个线程进入该lock之后的代码块 queueLock.unlock(); //释放锁,允许其他一个线程进入lock代码块 boolean boo = lock.hasQueuedThreads(); //判断是否有线程正在等待获取这个锁 int len = lock.getQueueLength(); //有几个线程正在等待获取这个锁 Collection<Thread> lockedThreads = lock.getThreads(); //返回所有正在等待获取这个锁的线程
|
只允许单个线程执行某个代码块 |
9 |
ReadWriteLock |
读写锁 |
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock(); //写锁为独占锁,只能允许单个线程进入临界区(之后的代码块),并且排斥读锁 lock.readLock().unlock(); //释放写锁,允许其他单个线程进入临界区 lock.writeLock().lock(); //读锁为共享锁,允许多个线程进入临界区,但是如果写锁被某线程占用,则读锁获取失败,线程进入阻塞。 lock.writeLock().unlock();//写锁释放
|
在多个线程间需要同步值,并且读写互斥 |
10 |
Condition |
条件锁 |
ReentrantLock lock = new ReentrantLock(true); //lock同步锁 Condition c = lock.newCondition(); //条件锁要在lock同步锁临界区中才会起作用 c.await(); //使当前线程进入睡眠状态 c.signalAll(); //唤醒使用了c这个对象进入睡眠的所有线程 c.signal(); 唤醒一个等待的线程。如果有线程在此条件下等待,那么将选择一个线程进行唤醒。
|
使线程有条件地进入睡眠状态,有条件的唤醒 |