一、多线程之间的通信(Java版本)

1、多线程概念介绍

多线程概念

2、线程之间如何通信

引入

Java 线程通信的方式

3、线程通信方法详细介绍

主要介绍wait/notify,也有ReentrantLock的Condition条件变量的await/signal,LockSupport的park/unpark方法,也能实现线程之间的通信。主要是阻塞/唤醒通信模式。

首先说明这种方法一般都是作用于调用方法的所在线程。比如在主线程执行wait方法,就是将主线程阻塞了。

wait/notify机制

await/signal

park/unpark

二、线程通信过程中需要注意的问题

1、唤醒丢失

如果一个线程先于被通知线程调用wait()前调用了notify(),等待的线程将错过这个信号。

核心代码演示

	    // 线程一使用LOCK1对象调用wait方法阻塞自己
        executor.execute(new ThreadTest("线程一",LOCK1,LOCK2));
        synchronized (LOCK1) {
            System.out.println("main执行notify方法让线程一醒过来");
            LOCK1.notify();
        }

2、假唤醒

由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。

3、多线程唤醒

		synchronized (waitName) {
            while (!flag.getFlag()) {
                try {
                    // 将标志位设置为TRUE
                    flag.setFlag(Constants.WaitOrNoWait.WAIT.getFlag());
                    System.out.println("name;"+name+" 我睡着了进入阻塞状态" + "flag = " + flag.getFlag());
                    waitName.wait();
                    System.out.println("name;"+name+" 我醒来了" + "flag = " + flag.getFlag());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
	private final static Object LOCK1 = new Object();
    private final static Object LOCK2 = new Object();
    private final  static Constants.WaitStatus FLAG = new Constants.WaitStatus(false);
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 1, TimeUnit.DAYS, new ArrayBlockingQueue<>(4), new ThreadPoolExecutor.AbortPolicy());
        executor.execute(new ThreadTest("线程一",LOCK1,LOCK2, FLAG));
        // ···唤醒
    }
class ThreadTest implements Runnable { //阻塞··· }

完整代码可以看这[Gitee仓库完整代码][https://gitee.com/malongfeistudy/javabase/tree/master/Java多线程_Study/src/main/java/com/mlf/thread/demo_wait_notify]

三、线程通信实战

前置知识:线程池的使用方法

    /**
     * 每个使用对应唯一的对象作为监视器对象锁。
     */
    public static final Object A_O = new Object();
    public static final Object B_O = new Object(); 
        /** 参数:
         * int corePoolSize,                     核心线程数
         * int maximumPoolSize,                  最大线程数
         * long keepAliveTime,                   救急存活时间
         * TimeUnit unit,                        单时间位
         * BlockingQueue<Runnable> workQueue,    阻塞队列
         * RejectedExecutionHandler handler      拒绝策略
         **/
        // 使用阿里巴巴推荐的创建线程池的方式
        // 通过ThreadPoolExecutor构造函数自定义参数创建
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                3,
                5,
                1,
                TimeUnit.DAYS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.AbortPolicy());
class ThreadDiy implements Runnable {
    private final String name;
    /**
     * 阻塞锁对象  等待标记
     **/
    private final Object waitFor;
    /**
     * 执行锁对象  下一个标记
     **/
    private final Object next;
    public AlternateThread(String name, Object waitFor, Object next) {
    }
    @Override
    public void run() {
        // 线程的代码逻辑···
    }
}

1、控制两个线程之间的执行顺序

题目:现在有两个线程,不论线程的启动顺序,我需要指定线程一先执行,然后线程二再执行。

        // 使用线程池创建线程
        executor.execute(new DiyThread(1, ONE_LOCK, TWO_LOCK));
        executor.execute(new DiyThread(2, TWO_LOCK, ONE_LOCK));
        synchronized (ONE_LOCK) {
            ONE_LOCK.notify();
        }

创建线程类

2、多线程交替打印输出

题目需求:现在需要使用三个线程轮流打印输出。说白了也就是多线程轮流执行罢了,和问题一控制两个线程打印顺序没什么区别

    /**
     * 阻塞锁对象  等待标记
     **/
    private final Object waitFor;
    /**
     * 唤醒锁对象  下一个标记
     **/
    private final Object next;
/**
 * 模拟执行流程
 * 打印名(name)    等待标记(waitFor)   下一个标记(next)
 *      1                 A                  B
 *      2                 B                  C
 *      3                 C                  A
 * 
 * 像不像Spring的循环依赖:确实很像,Spring中的循环依赖就是 BeanA 依赖 BeanB,BeanB 依赖 BeanA;
 * 他们实例化过程中都需要先属性注入对方的实例,倘若刚开始的时候都没有实例化,初始化就会死等。类似于死锁。
 **/

3、多线程顺序打印同一个自增变量

使用多线程轮流打印 01234····

具体代码请移步到Gitee仓库:[顺序打印自增变量][https://gitee.com/malongfeistudy/javabase/blob/master/Java多线程_Study/src/main/java/com/mlf/thread/print/AddNumberPrint2.java]

条件变量Condition的使用

如有问题,请留言评论。

发表回复