传统Synchronized版
/**
* 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
* 线程之间交替进行 线程A 线程B 同时操作一个变量number
* 线程A +1
* 线程B -1*/
public class Test01 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//判断等待、业务、通知
//数据,资源类
class Data{
private int number = 0;
//加1操作
public synchronized void increment() throws InterruptedException {
if(number != 0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知其他线程
this.notifyAll();
}
//减1操作
public synchronized void decrement() throws InterruptedException {
if (number == 0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知其他线程
this.notifyAll();
}
}
问题:上诉代码只是跑了A和B两个线程,请问,若再多几个线程,如四个线程,8个线程,线程是否还安全?
答案是不安全的。那应该如何解决呢?
虚假唤醒
jdk1.8官方文档对wait有一下的一个说明:
所以较上面的而言,为防止出现这种情况,我们应该在while循环中使用。
/**
* 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
* 线程之间交替进行 线程A 线程B 同时操作一个变量number
* 线程A +1
* 线程B -1*/
public class Test01 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0; i<5; i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//判断等待、业务、通知
//数据,资源类
class Data{
private int number = 0;
//加1操作
public synchronized void increment() throws InterruptedException {
while (number != 0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知其他线程
this.notifyAll();
}
//减1操作
public synchronized void decrement() throws InterruptedException {
while (number == 0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"===>"+number);
//通知其他线程
this.notifyAll();
}
}
JUC版
同样的,jdk官方文档给出了替代:
好了,我们直接上代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程之间的通信问题:生产者消费者问题! 等待唤醒,通知唤醒
* 线程之间交替进行 线程A 线程B 同时操作一个变量number
* 线程A +1
* 线程B -1*/
public class Test02 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.increment();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.decrement();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.increment();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
data.decrement();
}
}, "D").start();
}
}
//判断等待、业务、通知
//数据,资源类
class Data2 {
private int number = 0;
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
//加1操作
public void increment() {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "===>" + number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//减1操作
public void decrement() {
lock.lock();
try {
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "===>" + number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 实现精准通知唤醒
/**
* 线程A、B、C按顺序执行
* A 执行完通知唤醒 B
* B 执行完通知唤醒 C
* C 执行完通知唤醒 A*/
public class Test03 {
public static void main(String[] args) {
Data03 data = new Data03();
new Thread(()->{
for (int i=0; i<10; i++){
data.printA();
}
},"A").start();
new Thread(()->{
for (int i=0; i<10; i++){
data.printB();
}
},"B").start();
new Thread(()->{
for (int i=0; i<10; i++){
data.printC();
}
},"C").start();
}
}
//资源类
class Data03{
final Lock lock = new ReentrantLock();
final Condition condition1 = lock.newCondition();
final Condition condition2 = lock.newCondition();
final Condition condition3 = lock.newCondition();
private int number = 1;//标志位 1->A 2->B 3->C
public void printA(){
lock.lock();
try {
//业务,判断是否等待 ==> 执行 ==> 通知
while (number != 1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"===>AAAA");
//唤醒指定的线程B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务,判断是否等待 ==> 执行 ==> 通知
while (number != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"===>BBBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务,判断是否等待 ==> 执行 ==> 通知
while (number != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"===>CCCC");
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}