线程间通信
生产者消费者复习
package cduck.cn;
/**
* 题日:现在两个线程,可以操作初始值为零的一个交量,
* 实现一个线程对该变量加,一个线程对该变量减1,
* 实现交替,来10轮,变量初始值为0。
*
* 1、高内聚低耦合前提下,线程操作资源类
* 2、判断/干活/通知
* 3、防止虚假唤醒,只能用WHILE循环
*/
class SharingResource{//资源类
private int num=0;
public synchronized void increment() throws InterruptedException {
//判断
if (num!=0){
this.wait();
}
//干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//判断
if (num==0){
this.wait();
}
//干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();
}
}
public class WaitNotify {
public static void main(String[] args) {
SharingResource SR=new SharingResource();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
发生问题
以上的例子在两个线程的时候运行正常。但 当有四个线程,两个加两个减就可能出现错误:多加了几次。
解决
首先来看一下问题原因:
换成4个线程会导致错误,虚假唤醒 原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次
sleep():睡的时候 执行权仍然握在自己手里,醒了继续运行
wait():睡的时候就把执行权让出去了
问题发生的图解
解决办法:
解决虚假唤醒:查看API,java.lang.Object 中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while
代码
class SharingResource{//资源类
private int num=0;
public synchronized void increment() throws InterruptedException {
//判断
while (num!=0){
this.wait();
}
//干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//判断
while (num==0){
this.wait();
}
//干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();
}
}
生产者-消费者模型 新实现
用了Condition的await();与signalAll();来代替wait();与notifyAll();
package cduck.cn;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 题日:现在两个线程,可以操作初始值为零的一个交量,
* 实现一个线程对该变量加,一个线程对该变量减1,
* 实现交替,来10轮,变量初始值为0。
*
* 1、高内聚低耦合前提下,线程操作资源类
* 2、判断/干活/通知
*/
class SharingResource1{//资源类
private int num=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public void increment() {
lock.lock();
try{
//判断
while (num!=0){
condition.await();
}
//干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
condition.signalAll();
}catch (Exception e){
}finally {
lock.unlock();
}
}
public synchronized void decrement() throws InterruptedException {
lock.lock();
try{
//判断
while (num==0){
condition.await();
}
//干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
condition.signalAll();
}catch (Exception e){
}finally {
lock.unlock();
}
}
}
public class NewProductiveConsumption {
public static void main(String[] args) {
SharingResource1 SR1=new SharingResource1();
new Thread(()->{
for (int i=0;i<10;i++){
SR1.increment();
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++){
SR1.increment();
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
SR1.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
精准调度
1、有顺序通知,需要有标识位
2、有一个锁Lock,3把钥匙Condition
3、判断标志位
4、输出线程名+第几次+第几轮
5、修改标志位,通知下一个
package cduck.cn;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 多线程之间按顺序调用,实项A->B->C
* 3个线程启动,要求如下
* AA打55次,BB打印10次,CC打印15次
* 接着
* AA打5次,BB打印10次,CC打印15次
* ......
* 来10轮
*
* ----------------
* 主要知识点:根据标志位来精确唤醒
*/
class ShareResource{
int flag=1;//标志位。1代表A 2代表B 3代表C
Lock lock=new ReentrantLock();
Condition c1=lock.newCondition();
Condition c2=lock.newCondition();
Condition c3=lock.newCondition();
public void Print5(){
lock.lock();
try{
//判断
while (flag!=1){
c1.await();
}
//干活
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//通知
flag=2;//A执行完了,把标志为切换为2
c2.signal();//由B的condition 去唤醒B
}catch (Exception e){
}finally {
lock.unlock();
}
}
public void Print10(){
lock.lock();
try{
//判断
while (flag!=2){
c2.await();
}
//干活
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//通知
flag=3;//B执行完了,把标志为切换为3
c3.signal();//由C的condition 去唤醒C
}catch (Exception e){
}finally {
lock.unlock();
}
}
public void Print15(){
lock.lock();
try{
//判断
while (flag!=3){
c3.await();
}
//干活
for (int i=0;i<15;i++){
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//通知
flag=1;//C执行完了,把标志为切换为1
c1.signal();//由A的condition 去唤醒A
}catch (Exception e){
}finally {
lock.unlock();
}
}
}
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResource sr=new ShareResource();
new Thread(()->{
for (int i=0;i<10;i++){
sr.Print5();
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
sr.Print10();
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++){
sr.Print15();
}
},"C").start();
}
}