流程图
读输入写输出
分析
- 多线程编程stop方法不安全
- 常见使用
interrupt
来中断线程,它的应用场景
2.1 线程处于阻塞状态,如使用了sleep
2.2 使用while(!isInterrupted()){……}
来判断线程是否被中断 - java每个对象有且只有一个内置的同步锁
- wait会释放锁,notify仅仅只是通知,不释放锁,同理sleep也不释放锁
notify()或者notifyAll()方法并不是真正释放锁,必须等到synchronized方法或者语法块执行完才真正释放锁
码上见
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
c1.start();
}
}
class CubbyHole {
private int contents;
private boolean avaliable = false;
public synchronized int get() {
while (avaliable == false) {
try {
wait();
} catch (InterruptedException e) {
}
}
avaliable = false;
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (avaliable == true) {
try {
wait();
} catch (InterruptedException e) {
}
}
contents = value;
avaliable = true;
notifyAll();
}
}
class Consumer extends Thread {
private CubbyHole cubbyHole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyHole = c;
this.number = number;
}
@Override
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyHole.get();
System.out.println("消费者 #" + this.number + " got: " + value);
}
}
}
class Producer extends Thread{
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number){
cubbyhole = c;
this.number = number;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("生产者 #"+this.number + "put: "+i);
try {
sleep((int)(Math.random()*100));
} catch (InterruptedException e) {
}
}
}
}
锁了谁
- 锁只有一把,可锁类对象,实例对象,实质只有一把,临界资源,共同操作的只有一段
- java用锁主要有三种方式
synchronized 同步代码块机制
Lock 显性锁机制
semphore 信号量,可访问自身的线程数,有尝试,有超时设置
用while作判断后wait
没错就是为了占用,wait会让当前共用资源this进入等待队列,但wait不占用当前对象锁。换而言之获取锁,等待阻塞同时释放锁。
就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait
用while主因:确保同步代码块在其它线程唤醒之后,仍能依据同样条件重新进行。换而言之,条件表达式在线程唤醒前后可能条件结果不一致,用while是为了保证在同一条件值下进入wait待入队列。
wait前后,if 判断条件表达式结果可能不一样(尤其条件计算涉及到共享资源的变量),唤醒之后条件变量产生了变化 ,它只会执行后续代码,会产生虚假唤醒现象。
例如 if xxx {wait; do}
有三个线程A,B,C使用同步代码块等待。 若A先抢到锁,xx为真时 等待,do这个事情本意是在真的情况下继续。在A等待时,B抢到了锁,然后C抢锁等待,假定这时B被换醒得锁,它继续do的,但do干了一件事,将xxx改为了假,然后A被唤醒继续执行,本来A…是要在xxx为真时做的事情,现在好了用if它直接跳过了条件。
用while确定唤醒前后的条件一致性,即确保时刻会进行条件计算,唤醒后仍然受唤醒前同样的条件约束。这种情况,一般是在后续代码对同一共享资源有依赖。在代码中即是对 avaliable
同一状态,才进行下一步操作。
简而言之,用if极大可能会产生唤醒后,条件状态失控。
代码执行
D:\code-base\java\java_learn>cd "d:\code-base\java\java_learn\" && javac -encoding utf-8 ProducerConsumerTest.java && java ProducerConsumerTest
生产者 #1put: 0
消费者 #1 got: 0
生产者 #1put: 1
消费者 #1 got: 1
生产者 #1put: 2
消费者 #1 got: 2
生产者 #1put: 3
消费者 #1 got: 3
生产者 #1put: 4
消费者 #1 got: 4
生产者 #1put: 5
消费者 #1 got: 5
生产者 #1put: 6
消费者 #1 got: 6
生产者 #1put: 7
消费者 #1 got: 7
生产者 #1put: 8
消费者 #1 got: 8
生产者 #1put: 9
消费者 #1 got: 9