多线程编程模板中
判断→干活→通知
synchronized实现
/**
* 现在有两个线程,可以操作初始值为零的一个变量,
* 实现一个线程对该变量加1,一个线程对该变量减1,
* 交替,来10轮。
*/
public class ThreadWaitNotifyDemo {
public static void main(String[] args){
AirConditioner air = new AirConditioner();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
air.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
air.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class AirConditioner {
private int num = 0;
public synchronized void increment() throws InterruptedException {
// 1.判断
if (num != 0) {
this.wait();
}
// 2.干活
num++;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// 3.通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
// 1.判断
if (num == 0) {
this.wait();
}
// 2.干活
num--;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// 3.通知
this.notifyAll();
}
}
多线程编程模板下
注意多线程之间的虚假唤醒
虚假唤醒
场景还原
以上述代码为例,将线程增加到4个,2+2-,每个线程操作10次资源类
因为线程的调度是操作系统跟CPU共同决定的,所以增加以下假设
假设第一次A1线程进来,判断为false,num变成1,A1线程出去,
假设又进来一个A2线程,判断为true,wait,交出线程控制权,
假设这个时候A1线程又进来了,判断为true,wait,交出线程控制权,
假设又进来一个A3线程,判断为true,wait,交出线程控制权,…,
一直到A10都进来了,判断为true,wait,交出线程控制权,
此时进来了一个B1线程,判断为false,num变成0,notifall(),唤醒所有线程,
*因为使用的if,所以唤醒的A系列线程不会重新判断,然后就出现了-1,-2,-3,-4,…,-10这种情况,
C、D线程同理
这就是虚假唤醒
*解决方法
多线程交互中,必须要防止多线程的虚假唤醒,即(判断只用while,不用if)
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要重新判断一次。
lock实现
class AirConditioner {
private int num = 0;
private Lock l = new ReentrantLock();
private Condition c = l.newCondition();
public void increment() {
l.lock();
try {
// 1.判断
while (num != 0) {
c.await();// this.wait();
}
// 2.干活
num++;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// 3.通知
c.signalAll();// this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
public void decrement(){
l.lock();
try {
// 1.判断
while (num == 0) {
c.await();
}
// 2.干活
num--;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// 3.通知
c.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
精确唤醒
-
有顺序通知,需要有标识位
-
有一个锁Lock,3把钥匙Condition
-
判断标志位
-
输出线程名+第几次+第几轮
-
修改标志位,通知下一个
例子
public class ThreadOrderAccess{
public static void main(String[] args){
ShareResource art = new ShareResource();
new Thread(() -> {
for(int i = 1; i <= 10; i++){
art.print5();
}
},"A").start();
new Thread(() -> {
for(int i = 1; i <= 10; i++){
art.print10();
}
},"B").start();
new Thread(() -> {
for(int i = 1; i <= 10; i++){
art.print15();
}
},"C").start();
}
}
/**
* 多线程之间按顺序调用,实现A→B→C
* 三个启动线程,要求如下:
* AA打印5次,BB打印10次,CC打印15次 10轮
*/
class ShareResource{
private int num = 1;
private Lock l = new ReentrantLock();
private Condition c1 = l.newCondition();
private Condition c2 = l.newCondition();
private Condition c3 = l.newCondition();
public void print5(){
l.lock();
try{
while(num != 1){
c1.await();
}
for(int i = 1; i <= 5; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
num = 2;
c2.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
l.unlock();
}
}
public void print10(){
l.lock();
try{
while(num != 2){
c2.await();
}
for(int i = 1; i <= 10; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
num = 3;
c3.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
l.unlock();
}
}
public void print15(){
l.lock();
try{
while(num != 3){
c3.await();
}
for(int i = 1; i <= 15; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
num = 1;
c1.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
l.unlock();
}
}
}