JUC-线程间通信

本文详细介绍了多线程编程中的资源同步方法,包括使用Object的wait/notify和ReentrantLock实现生产者消费者模式,以及有序线程执行的示例。重点讲解了如何避免虚假唤醒并确保正确通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 多线程编程步骤:
  1. 创建资源类,在资源类中封装属性和操作方法
  2. 如果是生产者消费者场景:在资源类的操作方法中,要完成 判断, 干活, 通知 操作
  3. 创建多个线程,调用资源类的操作方法
  4. 防止虚假唤醒,线程间通信场景下,资源类操作方法中的判断要使用while循环来判断,而不是if判断
  • 场景1: 假设有一个初始变量0,同时有两个用户线程,一个线程对变量进行加1,另一个对变量减1,两个线程交替执行(甚至更多线程)
    (1)使用Object对象的wait和notify来实现
package com.lchtest.juc.resource;

public class Share {
    private int number = 0;
    public synchronized  void incr() throws InterruptedException {
       while (number != 0){  // 注意,这里用if会造成虚假唤醒,导致不正确的结果
           this.wait(); // !=0 等待,==0 执行加1操作,然后唤醒其他线程
       }
        // 操作
        number ++;
        System.out.println(Thread.currentThread().getName() + " do incr, number:" + number);
        // 通知
        notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
       while (number != 1){
           this.wait();
       }
        number --;
        System.out.println(Thread.currentThread().getName() + " do decr, number:" + number);
        this.notifyAll();
    }
}
package com.lchtest.juc;
import com.lchtest.juc.resource.Share;

/**
 * 线程间通信编程步骤/ 生产者消费者模式
 * 1. 创建资源类,封装资源属性和操作方法
 * 2. 在资源类操作方法中:
 * (1)判断 (2) 干活 (3)通知
 * 3. 创建多个线程,调用资源类的操作方法
 * 4. 防止虚假唤醒,判断条件放到while中
 */
public class ThreadCommunitionTest {
    // 假设有两个用户线程,一个线程对变量进行加1,另一个对变量减1,两个线程交替执行
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程a").start();

        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程b").start();

        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程c").start();


        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程d").start();
    }
}

使用ReentrantLock来实现:

package com.lchtest.juc.resource;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ShareByLock {
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    // +1操作
    public void incr() throws InterruptedException {
        // 加锁
        lock.lock();
        try { // try块里面放具体操作: 判断,干活,通知
            while (number != 0){ // 判断
                condition.await();
            }
            number ++;         // 干活
            System.out.println(Thread.currentThread().getName() + " do incr, number:" + number);
            condition.signalAll(); // 通知,唤醒其他线程
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
    // 减1操作
    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while (number != 1){ // 判断,number不为1,则等待  number==1 则减1操作
                condition.await();
            }
            number --;
            System.out.println(Thread.currentThread().getName() + " do decr, number:" + number);
            condition.signalAll(); // 通知
        } finally {
            lock.unlock();
        }
    }
}

调用:

package com.lchtest.juc;

import com.lchtest.juc.resource.Share;
import com.lchtest.juc.resource.ShareByLock;

/**
 * 线程间通信编程步骤/ 生产者消费者模式
 * 1. 创建资源类,封装资源属性和操作方法
 * 2. 在资源类操作方法中:
 * (1)判断 (2) 干活 (3)通知
 * 3. 创建多个线程,调用资源类的操作方法
 * 4. 防止虚假唤醒,判断条件放到while中
 */
public class ThreadCommunitionTestByLock {
    // 假设有两个用户线程,一个线程对变量进行加1,另一个对变量减1,两个线程交替执行
    public static void main(String[] args) {
        ShareByLock share = new ShareByLock();
        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程a").start();

        new Thread(() -> {
            for (int i = 0 ; i < 10; i ++){
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程b").start();
    }
}
  • 场景2: 三个线程顺序执行如下操作: 线程A打印5次,线程B打印10次,线程C打印15次,循环10轮
package com.lchtest.juc.resource;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 三个线程顺序执行如下操作: 线程A打印5次,线程B打印10次,线程C打印15次,循环10轮
 */
public class ShareResource {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2  = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int flag = 1;
    public void print5(int loop) throws InterruptedException {
        lock.lock();//上锁
        try {
            //判断
            while (flag != 1){
                condition1.await();
            }
            // 操作
            for (int i = 1; i <= 5; i ++){
                System.out.println(Thread.currentThread().getName() + "::" + i + ",外层循环次数:" + loop );
            }
            flag = 2; // 修改标志位,通知线程2开始操作
            condition1.signalAll();
            // 通知
        } finally {
            lock.unlock(); //解锁
        }
    }
    public void print10(int loop) throws InterruptedException{
        lock.lock();
        try {
            while (flag !=2){
                condition2.await();
            }
            for (int i = 1; i <= 10; i++){
                System.out.println(Thread.currentThread().getName() + "::" + i + ",外层循环次数:"+ loop);
            }
            flag = 3;
            condition2.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public void print15(int loop) throws InterruptedException{
        lock.lock();
        try {
            while (flag !=3){
                condition3.await();
            }
            for (int i = 1; i <= 15; i++){
                System.out.println(Thread.currentThread().getName() + "::" + i + ",外层循环次数:"+ loop);
            }
            flag = 1;
            condition3.signalAll();
        } finally {
            lock.unlock();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值