Java并发编程—线程协作

并发编程—线程协作

这里会记录学习的内容有:等待通知(wait,notify/notifyAll),join()

等待和通知(wait()/notify()/notifyAll())

wait() / notify() / notifyAll()

之前说过wait() / notify() / notifyAll(),他们都是Object类中的方法,使用前都需要先获取锁
wait():使线程进入等待(阻塞)状态,可设置时间参数。
notify()/notifyAll():唤醒等待中的线程。

等待通知标准范式

等待方:

  1. 获取对象的锁;
  2. 循环里判断条件是否满足,不满足调用wait方法;
  3. 条件满足执行业务逻辑;

通知方:

  1. 获取对象的锁;
  2. 改变条件
  3. 通知所有等待在对象的线程

notify / notifyAll:在实际使用唤醒时尽量使用notifyAll(),防止通知信号丢失。

举个栗子

使用 wait() / notifyAll 完成一个通知的小栗子

    // 起始地
    private String SITE = "杭州";

    /**
     * 改变地点
     */
    public synchronized void changeSite() {
        // 将地点改为上海
        this.SITE = "上海";
        // 通知(唤醒所有等待的线程)
        notifyAll();
    }

    /**
     * 监听地点
     */
    public synchronized void waitSite() {
        // 监听地点,如果改变就往下执行业务
        while ("杭州".equals(this.SITE)) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // Core
        System.out.println("site is 上海");
    }

    public static void main(String[] args) throws InterruptedException {
        LogisticsDemo logisticsDemo = new LogisticsDemo();
        // 启动3个线程监听地点变化
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                logisticsDemo.waitSite();
            }).start();
        }
        // 主线程休眠3秒
        Thread.sleep(3000);
        // 改变地点
        logisticsDemo.changeSite();
    }

上面代码执行的情况:启动三个线程等待地点变化,主线程休眠三秒后将地点改为上海,三个线程都受到了通知,打印出 site is 上海。

等待超时模式

什么是超时等待?
调用一个方法时,等待一段时间(一般给定一个时间段),如果该方法能够在给定的时间段内得到结果,那么将结果立刻返回,反之,超时返回默认结果。这就是超时等待。

等待/通知的经典范式,即加锁,条件循环和处理逻辑三个步骤,而这种范式无法做到超时等待。
其实在等待通知的标准方式上加以改动就可以实现超时等待。
伪代码如下:

long future = System.currentTimeMillis() + mills;
long remaining = mills;
synchronized (lock) {
   while (!condition && remaining > 0) {
        wait(remaining);
        remaining = future - System.currentTimeMillis();
   }
       //处理代码
}
举个栗子

使用等待超时模式完成连接池拿取和放连接的方法;

    /**
     * 在指定超时时间内拿取连接,超时返回null
     *
     * @param mills 超时时间
     * @return
     */
    public Connection takeConn(Long mills) throws InterruptedException {
        synchronized (pool) {
            if (mills < 0) {
                if (pool.isEmpty()) {
                    // 如果池中是空的,则等待
                    pool.wait();
                }
                // 返回第一个连接
                return pool.removeFirst();
            } else {
                Long overtime = System.currentTimeMillis() + mills;
                Long remainning = mills;
                while (pool.isEmpty() && remainning > 0) {
                    // 连接池为空并且等待时间大于0
                    pool.wait(remainning);
                    // 需要等待的时间
                    remainning = overtime - System.currentTimeMillis();
                }
                Connection conn = null;
                if (!pool.isEmpty()) {
                    conn = pool.removeFirst();
                }
                return conn;
            }
        }
    }


    /**
     * 释放连接
     *
     * @param conn
     */
    public void releaseConn(Connection conn) {
        if (conn != null) {
            synchronized (pool) {
                // 将连接放回连接池
                pool.addLast(conn);
                // 通知等待的线程
                pool.notifyAll();
            }
        }
    }

join()

简单了解

join的单词意思时参加/加入。放在我们这里呢最合适的意思是,插入。join()方法简单来讲就是让线程去插队,完整的描述是这样的:线程A,执行了线程B的join()方法,线程A必须要等待B执行完成了以后,线程A才能继续自己的工作。这句话很好理解,下面就用代码表现出来。

扫描二维码关注公众号,回复: 5616681 查看本文章
举个栗子
    /**
     * 线程插队处理类
     */
    static class JoinThread implements Runnable {

        /**
         * 插队的线程
         */
        private Thread thread;

        public JoinThread(Thread thread) {
            this.thread = thread;
        }

        @Override
        public void run() {
            try {
                // 调用join()方法 【可以理解为插队的动作】
                this.thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " run complete.");
        }
    }


    public static void main(String[] args) throws InterruptedException {
        // 插队的线程
        Thread preThread = Thread.currentThread();
        for (int i = 0; i < 10; i++) {
            // 第一个插队的线程为 main
            Thread thread = new Thread(new JoinThread(preThread), String.valueOf(i));
            thread.start();
            System.out.println(preThread.getName() + " --> " + thread.getName());
            preThread = thread;
        }
        // 主线程插到了一个位置,也就是说main线程运行完其他线程才能往下执行【主线程的下一个线程是 线程 0】
        System.out.println(Thread.currentThread().getName() + " run complete.");
    }

上面代码运行的结果:

main --> 0
0 --> 1
1 --> 2
2 --> 3
3 --> 4
4 --> 5
5 --> 6
6 --> 7
7 --> 8
8 --> 9
0 run complete.
1 run complete.
2 run complete.
3 run complete.
4 run complete.
5 run complete.
6 run complete.
7 run complete.
8 run complete.
9 run complete.

可以看出join() 方法可以将线程并行改为串行的执行方式;
我这里只是写了一些join的概念和他的使用。
想深入了解的话请参照文章:https://www.cnblogs.com/aboutblank/p/3631453.html

猜你喜欢

转载自blog.csdn.net/qq_38345031/article/details/84554523