首先,我们知道线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的;
以客户卖包子为例,当店家在包子加工到第4步时就可以卖给客户为例说明,A,B两个线程,示例如下:
方式一:使用Object类的wait() 和 notify() 方法
众所周知,Object类提供了线程间通信的方法:wait()、notify()、notifyaAl(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。注意: wait和 notify必须配合synchronized使用;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wangyong on 2020/1/5.
*/
public class TestSyncDemo1 {
public static void main(String[] args) {
// 定义一个锁对象
Object lock = new Object();
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (int i = 1; i <= 5; i++) {
list.add("做包子"+i);
System.out.println("包子还在加工的第:" + i+"步");
if (list.size() == 4){
lock.notifyAll();// 唤醒B线程
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
if (list.size() != 4) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("通知客户可以下单买包子....");
break;
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
方式二,既然wait和notify配合synchronized使用能够实现,当然用 ReentrantLock 结合 Condition也可以完成;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by wangyong on 2020/1/5.
*/
public class TestSyncDemo2 {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
lock.lock();
for (int i = 1; i <= 5; i++) {
list.add("做包子"+i);
System.out.println("包子还在加工的第:" + i+"步");
if (list.size() == 4){
condition.signalAll();
System.out.println("通知客户可以下单买包子....");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
});
// 实现线程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 4) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程B收到通知,开始卖包子");
lock.unlock();
});
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
方式三:使用 volatile 关键字
import java.util.ArrayList;
import java.util.List;
/**
* Created by wangyong on 2020/1/4.
*/
public class TestSyncDemo3 {
// 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
static volatile boolean notice = false;
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
list.add("做包子"+i);
System.out.println("包子还在加工的第:" + i+"步");
if (list.size() == 4){
notice = true;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (notice) {
System.out.println("通知客户可以下单买包子....");
break;
}
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式
方式四:使用JUC工具类 CountDownLatch
jdk1.5之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了我们的并发编程代码的书写,CountDownLatch是基于AQS框架,相当于也是维护了一个线程间共享变量state
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* Created by wangyong on 2020/1/5.
*/
public class TestSyncDemo4 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
List<String> list = new ArrayList<>();
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
list.add("做包子"+i);
System.out.println("包子还在加工的第:" + i+"步");
if (list.size() == 4){
countDownLatch.countDown();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 实现线程B
Thread threadB = new Thread(() -> {
while (true) {
if (list.size() != 4) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("通知客户可以下单买包子....");
break;
}
});
// 需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
方式五:基本LockSupport实现线程间的阻塞和唤醒
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.LockSupport;
/**
* Created by wangyong on 2020/1/5.
*/
public class TestSyncDemo5 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 实现线程B
final Thread threadB = new Thread(() -> {
if (list.size() != 4) {
LockSupport.park();
}
System.out.println("通知客户可以下单买包子....");
});
// 实现线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
list.add("做包子"+i);
System.out.println("包子还在加工的第:" + i+"步");
if (list.size() == 4){
LockSupport.unpark(threadB);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadA.start();
threadB.start();
}
}
这是执行后的结果:
包子还在加工的第:1步
包子还在加工的第:2步
包子还在加工的第:3步
包子还在加工的第:4步
通知客户可以下单买包子....
包子还在加工的第:5步