1. 前言-题目
- 题目: 两个线程,可以操作初始值为零的一个变量,实现一个线程对该变量 +1 ,一个线程对该变量 -1 ,实现交替,进行 10 轮,变量初始值为0
2. 线程通信
- 生产者 + 消费者
- 消费者来消费,需要通知生产者先生产
- 生产者生产完毕,需要通知消费者消费
- 通知等待唤醒机制
3. 多线程模板(2)
1. 根据生产者 + 消费者的工作顺序得出
1. 判断(判断有无产品)
2. 干活(如果没有产品,则先生成 ; 如果已有产品,则先消费)
3. 通知(通知对方我已经干完了,该你了)
2. 口诀(2):判断 - 干活 - 通知
3. 模板
public class JUC02_communication {
public static void main(String[] args) {
NUM num = new NUM();
new Thread(()->{
for (int i = 0; i < 10; i++){
try {
num.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Thread_A").start();
new Thread(()->{
for (int i = 0; i < 10; i++){
try {
num.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Thread_B").start();
}
}
class NUM{
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "\t"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t"+number);
this.notifyAll();
}
}
3. 多线程模板(3)
1. 上述代码的问题
- 如果题目修改一下—— 两个线程执行加操作,两个线程执行减操作
- 换成4个线程会导致错误,虚假唤醒
2. 原因
- 在java多线程判断时,不能用if,程序出事出在了判断上面
- 突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次
3. 解决办法
- 解决虚假唤醒:查看API,java.lang.Object
- 中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
4. 口诀(3):多线程交换中,必须防止多线程的虚假唤醒,也即线程中判断只能用 while
5. 修改后的代码
class NUM{
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "\t"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t"+number);
this.notifyAll();
}
}
4. 线程通信中 synchronized 与 lock
1. synchronized
- synchronized 实现线程通信时,是使用 wait - notify / notifyAll
- 示意图
2. lock
- lock 使用的是 Condition 对象
- 示意图
3. 模板(4)- lock
class NUM{
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();
}
number++;
System.out.println(Thread.currentThread().getName() + "\t"+number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "\t"+number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
5. 线程间定制化调用通信(精准唤醒)
1. 题目
- 多线程之间按顺序调用
- 也就是 A 打印 1 次, B 打印 2 次, C 打印 3 次,一共进行 10 轮,且严格按照 A -> B -> C的顺序
2. 口诀(4):注意标志位的修改和定位
3. 代码
public class JUC03_CustomizedCommunication {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{shareResource.printA();},"Thread_A").start();
new Thread(()->{shareResource.printB();},"Thread_BB").start();
new Thread(()->{shareResource.printC();},"Thread_CCC").start();
}
}
class ShareResource{
private int state = 0;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void printA() {
print(0, conditionA, conditionB);
}
public void printB() {
print(1, conditionB, conditionC);
}
public void printC() {
print(2, conditionC, conditionA);
}
private void print(int currentState, Condition currentCondition, Condition nextCondition) {
for (int i = 0; i < 10; ) {
lock.lock();
try {
while (state % 3 != currentState) {
currentCondition.await();
}
for (int j = 0; j < (state % 3 + 1); j++){
System.out.println(Thread.currentThread().getName() + " print " + (j+1));
}
state++;
i++;
nextCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}