线程间通信
生成者消费者模型
/**
* @author phd
* @version 1.0
* @date 2020/8/31 22:36
* 生产者消费者模型练习
*/
class AirConditioner{
private int number = 0;
public synchronized void increment() throws InterruptedException {
if(number != 0){//1.判断 ,存在虚假唤醒 if 改成 while
this.wait();
}
//2.干活
number++;
//3.通知
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
public synchronized void decreament() throws InterruptedException {
if(number == 0){ //存在虚假唤醒 if 改成 while
this.wait();
}
number-- ;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
public class ThreadWaitNotifyDemo {
public static void main(String args[]) {
AirConditioner airConditioner = new AirConditioner();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
airConditioner.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
airConditioner.decreament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
airConditioner.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
airConditioner.decreament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
虚假唤醒
以上代码,换成4个线程会导致错误。
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一个添加的线程进到了if,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次。
解决办法
解决虚假唤醒:查看API,java.lang.Object
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
用Lock实现生成者消费者模型
Condition 接口
condition可以理解为条件队列。当一个线程在调用了await方法后,直到线程等待的某个条件为真的时候为会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition一般都是作为Lock的内部实现。
- await():造成当前线程在接到信号或被中断之前一直处于等待状态。
- await(long time, TimeUint unit):造成当前线程在接到信号、被中断或到达等待时间之前一直处于等待状态。
- awaitNanos(long nanosTimeout):造成当前线程在接到信号、被中断或达到指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值= nanosTimeout - 消耗时间,如果返回值 <=0,则可以认定它已经超时了。
- awaitUninterruptibly():造成当前线程在接到信号之前一直处于等待状态。【该方法对中断不敏感】
- awaitUntil(Date deadline):造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回值false。
- singal():唤醒一个等待线程,该线程从等待方法返回前必须获得与Condition相关的锁。
- singalAll():唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
class AirContainer{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try{
while (number != 0){
condition.await();
}
//2.干活
number++;
//3.通知
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
}finally {
lock.unlock();
}
}
public void decreament() throws InterruptedException {
lock.lock();
try{
while (number == 0){
condition.await();
}
//2.干活
number--;
//3.通知
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
public class LockThreadWaitNotifyDemo {
public static void main(String args[]){
AirContainer airContainer = new AirContainer();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
airContainer.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
airContainer.decreament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
airContainer.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
airContainer.decreament();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
线程间定制化调用通信例子
/**
*
* @Description:
* 多线程之间按顺序调用,实现A->B->C
* 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次
* 接着
* AA打印5次,BB打印10次,CC打印15次
* ......来10轮
*
*/
/**
* 解决思路
* 1、有顺序通知,需要有标识位
*
* 2、有一个锁Lock,3把钥匙Condition
*
* 3、判断标志位
*
* 4、修改标志位,通知下一个
**/
class PrintResource{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try{
while (number != 0){
condition1.await();
}
for (int i = 0; i < 5 ; i++) {
System.out.println(Thread.currentThread().getName());
}
number = 1;
condition2.signal(); //用下一把唤醒
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try{
while(number != 1){
condition2.await();
}
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName());
}
number = 2;
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try{
while(number != 2){
condition3.await();
}
for (int i = 0; i < 15 ; i++) {
System.out.println(Thread.currentThread().getName());
}
number = 0;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class TreadLoop {
public static void main(String args[]){
PrintResource printResource = new PrintResource();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
printResource.printA();
}
},"A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
printResource.printB();
}
},"B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
printResource.printC();
}
},"C").start();
}
}