????????线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析 :
????????场景---两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信
?
一、synchronized实现?
/**
* 实现线程A对一个值+1,线程B对该值-1
*/
//第一步:创建资源类,定义属性和操作方法
class Share{
//目标值
int number = 0;
//+1操作
public synchronized void incr() throws InterruptedException {
//第二步:判断->操作->通知
//判断
if (number != 0){
this.wait();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+":"+number);
//通知其他线程
this.notifyAll();
}
//-1操作
public synchronized void decr() throws InterruptedException {
if (number != 1){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+":"+number);
this.notifyAll();
}
}
public class ThreadDemo01 {
//第二步,创建多个线程,调用资源类中的操作方法
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程B").start();
}
}
?
二、虚假唤醒问题?
? ? ? ? 现在我们将进程变为四个,A、C进程负责加,B、D进程负责减??
//第一步:创建资源类,定义属性和操作方法
class Share{
//目标值
int number = 0;
//+1操作
public synchronized void incr() throws InterruptedException {
//第二步:判断->操作->通知
//判断
if (number != 0){
this.wait();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+":"+number);
//通知其他线程
this.notifyAll();
}
//-1操作
public synchronized void decr() throws InterruptedException {
if (number != 1){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+":"+number);
this.notifyAll();
}
}
public class ThreadDemo01 {
//第二步,创建多个线程,调用资源类中的操作方法
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程D").start();
}
}
?
?????????????????????????????????????????????????????????????????????????????
????????我们可以看到,结果并不是严格的一加一减,而是出现了2、3这样的数字,这是为什么呢?
? ? ? ? 这就是要讲的虚假唤醒的问题。
? ? ? ? 查阅jdk1.8版本的API,可以看到wait方法中有这样一段话:
? ? ? ? ?意思是,我们代码中的wait应该放在循环中来避免虚假唤醒,不应该写成:
if (number != 0){
this.wait();
}
? ? ? ? 而应该写成:
while (number != 0){
this.wait();
}
? ? ? ? 为什么会导致这样呢?什么是虚假唤醒呢?
? ? ? ? 简而言之,就是wait()方法在唤醒之后,会直接在之前等待的地方继续执行,而不会再执行前面的判断了,这就叫做虚假唤醒。所以要放在循环中,让他唤醒后重新去做判断,避免虚假唤醒的问题。
? ? ? ? 所以Share类中正确的代码应是:
class Share{
//目标值
int number = 0;
//+1操作
public synchronized void incr() throws InterruptedException {
//第二步:判断->操作->通知
//判断
while (number != 0){
this.wait();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+":"+number);
//通知其他线程
this.notifyAll();
}
//-1操作
public synchronized void decr() throws InterruptedException {
while (number != 1){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+":"+number);
this.notifyAll();
}
}
? ? ? ? 运行结果:
线程A:1
线程B:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程A:1
线程B:0
线程A:1
线程B:0
线程A:1
线程B:0
Process finished with exit code 0
?
?
三、Lock实现四线程操作
/**
* Lock实现:线程A、C将number值从0变为1,线程B、D将number值从1变为0
*/
class Share{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1操作
public void incr() throws InterruptedException {
lock.lock();
try{
while(number != 0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+":"+number);
condition.signalAll();
}finally {
lock.unlock();
}
}
//-1操作
public void decr() throws InterruptedException {
lock.lock();
try {
while(number != 1){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+":"+number);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程D").start();
}
}
?
? ? ? ? 运行结果:
线程A:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程C:1
线程B:0
|