1. 等待唤醒
概念: 生产消费模型(观察者设计模式)中有两种角色,一个是生产者,负责生产资源,一个是消费者,负责使用资源,该模型的特点是,当无资源时,消费者应该阻塞并等待生产,当有资源时,生产者应该阻塞并等待消费。
- 生产消费模型方法:以下三个方法均来自Object类,且只能在同步代码区中使用:
锁实例.wait():让某个线程等待,此时该线程会加入到等待队列进行等待。锁实例.notify():随机唤醒等待队列中的一个线程,不释放锁。锁实例.notifyAll():唤醒等待队列中的所有等待线程,不释放锁。
wait()vssleep():wait()是Object类的成员方法,sleep()是Thread类的静态方法。wait()释放了锁,其他线程可以进入同步代码块,sleep()不释放锁。wait()必须在同步代码块中调用,sleep()可以在任何地方调用。wait()可以被唤醒notify(),sleep()只能等待计时结束或者被打断interreput()。
源码: /javase-advanced/
- src:
c.y.thread.communication.WaitNotifyTest
2. 停止线程
概念: 想停止一个线程,只能等待线程体自然结束,如果要停止一个挂起,如 wait() 或sleep() 状态的线程,则需要使用 interrupt() 进行打断并获取一个异常,然后在异常处理 catch{} 中决定是否要终止线程,但是并不建议使用 interrupt() 来控制业务逻辑。
stop()方法已经过时,容易产生线程状态的不一致。
源码: /javase-advanced/
- src:
c.y.thread.communication.StopThreadTest
/**
* @author yap
*/
public class WaitNotifyTest {
@Data
private static class Food {
private String name;
private String type;
private boolean exist;
}
private static class AsyncProducer implements Runnable {
private final Food food;
private boolean isEnglish;
AsyncProducer(Food food) {
this.food = food;
}
@Override
public void run() {
while (true) {
if (isEnglish) {
food.setName("cake");
food.setType("mickey");
} else {
food.setName("dan-gao");
food.setType("mi-qi");
}
isEnglish = !isEnglish;
}
}
}
private static class AsyncConsumer implements Runnable {
private final Food food;
AsyncConsumer(Food food) {
this.food = food;
}
@SneakyThrows
@Override
public void run() {
while (true) {
TimeUnit.SECONDS.sleep(1L);
System.out.println(food.getName() + ": " + food.getType());
}
}
}
private static class SyncProducer implements Runnable {
private final Food food;
private boolean isEnglish;
SyncProducer(Food food) {
this.food = food;
}
@Override
public void run() {
while (true) {
// Don't use "synchronized (this)"
synchronized (food) {
if (isEnglish) {
food.setName("cake");
food.setType("mickey");
} else {
food.setName("dan-gao");
food.setType("mi-qi");
}
isEnglish = !isEnglish;
}
}
}
}
private static class SyncConsumer implements Runnable {
private final Food food;
SyncConsumer(Food food) {
this.food = food;
}
@SneakyThrows
@Override
public void run() {
while (true) {
synchronized (food) {
TimeUnit.MILLISECONDS.sleep(300L);
System.out.println(food.getName() + ": " + food.getType());
}
}
}
}
private static class OneByOneProducer implements Runnable {
private final Food food;
private boolean isEnglish;
OneByOneProducer(Food food) {
this.food = food;
}
@SneakyThrows
@Override
public void run() {
while (true) {
synchronized (food) {
if (food.isExist()) {
food.wait();
} else {
if (isEnglish) {
food.setName("cake");
food.setType("mickey");
} else {
food.setName("dan-gao");
food.setType("mi-qi");
}
isEnglish = !isEnglish;
food.setExist(true);
food.notify();
}
}
}
}
}
private static class OneByOneConsumer implements Runnable {
private final Food food;
OneByOneConsumer(Food food) {
this.food = food;
}
@Override
public void run() {
while (true) {
synchronized (food) {
try {
if (food.isExist()) {
TimeUnit.SECONDS.sleep(1L);
System.out.print(food.getName() + ": " + food.getType() + "\n");
food.setExist(false);
food.notify();
} else {
food.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
private Food food = new Food();
@Test
public void asynchronousVersion() {
new Thread(new AsyncProducer(food)).start();
new Thread(new AsyncConsumer(food)).start();
}
@Test
public void synchronizedVersion() {
new Thread(new SyncProducer(food)).start();
new Thread(new SyncConsumer(food)).start();
}
@Test
public void oneByOneVersion() {
new Thread(new OneByOneProducer(food)).start();
new Thread(new OneByOneConsumer(food)).start();
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
3. Condition
概念: Condition的本质就是多个等待队列,可以将线程放入指定的队列中,也可以从指定的队列中随机唤醒一个或者全部线程,Condition实例需要通过Lock接口的 newCondition() 来创建。
- Condition方法:
void await():让线程进入指定的等待队列中。void signal():从指定的等待队列中,随机唤醒一个await()状态的线程。void signalAll():从指定的等待队列中,唤醒所有await()状态的线程。
源码: /javase-advanced/
- src:
c.y.thread.communication.ConditionTest
/**
* @author yap
*/
public class ConditionTest {
private static class ConditionDemo {
private List<String> list = new ArrayList<>();
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(String data) {
int max = 10;
lock.lock();
try {
while (list.size() == max) {
producer.await();
}
list.add(data);
consumer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public String get() {
String result = null;
lock.lock();
try {
while (list.size() == 0) {
consumer.await();
}
result = list.remove(0);
producer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return result;
}
}
@SneakyThrows
@Test
public void condition() {
ConditionDemo conditionDemo = new ConditionDemo();
for (int i = 0, j = 10; i < j; i++) {
new Thread(() -> {
for (int m = 0, n = 5; m < n; m++) {
System.out.println(Thread.currentThread().getName() + " got: " + conditionDemo.get());
}
}, "consumer-" + i).start();
}
TimeUnit.SECONDS.sleep(2L);
for (int i = 0, j = 2; i < j; i++) {
new Thread(() -> {
for (int m = 0, n = 25; m < n; m++) {
conditionDemo.put(Thread.currentThread().getName() + ": " + m);
}
}, "producer-" + i).start();
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
4. LockSupport
概念: LockSupport是并发包中提供的更灵活的控制线程等待和唤醒的工具类,它可以更精准地唤醒指定的某个线程。
- LockSupport方法:
static void park():将当前线程停驻。static void unpark(Thread thread):放行指定的线程。
unpack()如果在pack()之前被调用,则pack()会失效。
源码: /javase-advanced/
- src:
c.y.thread.communication.LockSupportTest
/**
* @author yap
*/
public class LockSupportTest {
@SneakyThrows
@Test
public void lockSupport() {
Thread thead = new Thread(() -> {
for (int i = 0, j = 10; i < j; i++) {
System.out.println(Thread.currentThread() + ": " + i);
if (i == 5) {
LockSupport.park();
}
}
});
thead.start();
TimeUnit.SECONDS.sleep(8L);
System.out.println("after 8s...");
LockSupport.unpark(thead);
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
5. CountDownLatch
概念: CountDownLatch,意为倒数的门闩,用来对线程进行倒数计数。
- CountDownLatch关注的是门闩数,门闩数为0时放行,不关注线程数,一个线程可能或打开多个门闩。
- CountDownLatch一般用于某个线程等待若干个其他线程执行完任务之后,它才执行,不可重复使用。
- 方法:
void await():将门闩栓在此处,当前线程进入阻塞等待状态,只有门闩计数器为0时才会放行。void countDown():门闩计数器减一。long getCount():获取当前门闩数。
- 应用场景:启动三个线程计算,需要对结果进行累加。
源码: /javase-advanced/
- src:
c.y.thread.communication.CountDownLatch
/**
* @author yap
*/
public class CountDownLatchTest {
@Test
public void countDownLatch() {
CountDownLatch countDownLatch = new CountDownLatch(8);
new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("competition is over...");
}).start();
for (int i = 0, j = 8; i < j; i++) {
long sleepTime = i;
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(sleepTime);
System.out.println(Thread.currentThread().getName()
+ ": reach and current count is "
+ countDownLatch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
6. CyclicBarrier
概念: CyclicBarrier表示一个循环的屏障,构造的时候需要设定一个最大值,每个线程在抵达屏障处的时候都会阻塞,当抵达的线程数达到屏障指定值时,屏障将被推倒,所有线程一起放行。
- CyclicBarrier关注的是线程数,线程数达到指定值时放行。
- CyclicBarrier可循环使用。
- 构造:
CyclicBarrier(int parties, Runnable barrierAction):- param1: 指定屏障前阻塞线程的最大值,屏障前的线程数递增到这个值时放行。
- param2: 当屏障被打破后执行的任务。
CyclicBarrier(int parties):不指定屏障被打破后执行的任务。
- 方法:
void await():模拟一个线程达到了屏障处,阻塞等待。int getNumberWaiting():返回屏障前的等待线程的数量。
源码: /javase-advanced/
- src:
c.y.thread.communication.CyclicBarrierTest
/**
* @author yap
*/
public class CyclicBarrierTest {
@Test
public void cyclicBarrier() {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
System.out.println("Enough for 3 people,start playing cards...");
});
for (int i = 0, j = 8; i < j; i++) {
long sleepTime = i;
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(sleepTime);
System.out.println(Thread.currentThread().getName()
+ ": ready..."
+ cyclicBarrier.getNumberWaiting());
cyclicBarrier.await();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": playing...");
}).start();
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
7. Phaser
概念: Phaser表示一个分阶段的一次性屏障组,我们可以 Phaser 类前进方法来自定义阶段处理内容。
- 重写前进方法:
boolean onAdvance(int phase, int registeredParties):- 前进方法在每次进入一个新阶段的时候自动调用。
- param1: 当前阶段标志,由整数表示,0表示第一阶段,以此类推。
- param2: 晋级线程数,即当前阶段下仍处于注册状态的线程数。
- return: false表示当前阶段结束,true表示所有阶段结束。
- 构造:
Phaser():构建一个默认值为0的Phaser。Phaser(int parties):构建一个指定数量的Phaser。
- 方法:
int arriveAndAwaitAdvance():成功抵达当前阶段并且等待前进(进入下一阶段)。int arriveAndDeregister():成功抵达当前阶段并且从屏障组中注销。int bulkRegister(int parties):指定初始注册线程数,await线程数量达到parties值便触发前进方法,表示进入下一阶段。
源码: /javase-advanced/
- src:
c.y.thread.communication.PhaserTest
/**
* @author yap
*/
public class PhaserTest {
private static Phaser phaser;
@Before
public void before() {
phaser = new Phaser() {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
final int levelOne = 1;
final int levelTwo = 2;
if (phase == 0) {
System.out.println(registeredParties + " hero ready...\n");
return false;
}
if (phase == levelOne) {
System.out.println(registeredParties + " hero passed level one...\n");
return false;
}
if (phase == levelTwo) {
System.out.println(registeredParties + " hero passed level two...\n");
return false;
}
System.out.println("over...");
return true;
}
};
}
private static class Hero implements Runnable {
private int heroLevel;
private Hero(int heroLevel) {
this.heroLevel = heroLevel;
}
@SneakyThrows
private void ready() {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName() + " ready...");
phaser.arriveAndAwaitAdvance();
}
@SneakyThrows
private void levelOne() {
final int levelLimit = 2;
if (heroLevel > levelLimit) {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName() + " passed level one...");
phaser.arriveAndAwaitAdvance();
}
}
@SneakyThrows
private void levelTwo() {
final int levelLimit = 4;
if (heroLevel > levelLimit) {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName() + " passed level two...");
phaser.arriveAndAwaitAdvance();
}
// Can only be written in the last level
System.out.println(Thread.currentThread().getName() + " deregister...");
phaser.arriveAndDeregister();
}
@Override
public void run() {
ready();
levelOne();
levelTwo();
}
}
@Test
public void phaser() {
final int parties = 6;
phaser.bulkRegister(parties);
for (int i = 0; i < parties; i++) {
new Thread(new Hero(i + 1), "hero-" + i).start();
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
8. Semaphore
概念: Semaphore表示信号量,可以控制最大并发数量,比如在构造的时候设置信号量为2,那么表示最多只允许两个线程并发运行,主要用于限流。
- 构造:
Semaphore(int permits):构造一个指定初始信号数量的信号灯。Semaphore(int permits, boolean fair):指定是公平还是非公平信号,默认非公平。
- 方法:
void acquire():获取一个信号,获取不成功当前线程会阻塞等待。void release():释放一个信号,此时其他线程可以重新获取这个信号。
源码: /javase-advanced/
- src:
c.y.thread.communication.SemaphoreTest
/**
* @author yap
*/
public class SemaphoreTest {
@Test
public void semaphore() {
Semaphore semaphore = new Semaphore(2);
for (int i = 0, j = 10; i < j; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " start...");
TimeUnit.SECONDS.sleep(5L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}).start();
}
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}
9. Exchanger
概念: Exchanger用于交换数据,它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。
- 构造:
Exchanger():Exchanger在构造的时候需要指定泛型,表示交换的数据的类型。
- 方法:
V exchange(V x):交换数据,方法会阻塞,直到第二个线程调用exchange()。- param1: 发送出去的数据。
- return: 接收回来的数据。
- Exchanger需要作用于成对儿的线程,适用于两个线程之间的通信,如交易装备过程。
源码: /javase-advanced/
- src:
c.y.thread.communication.ExchangerTest
/**
* @author yap
*/
public class ExchangerTest {
@Test
public void exchange() {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3L);
System.out.println("Buyer ready...");
String exchangedValue = exchanger.exchange("10 yuan");
System.out.println("Buyer got " + exchangedValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("Seller ready...");
String exchangedValue = exchanger.exchange("bread");
System.out.println("Seller got " + exchangedValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
@SneakyThrows
@After
public void after() {
System.out.println(System.in.read());
}
}