学习笔记五:线程间的协做与通讯

常见的线程之间通讯方式有以下几种:java

一、wait和notify/notifyAll并发

二、await和signal/signalAllide

三、sleep/yield/join工具

四、同步屏障CyclicBarrier学习

五、CountDownLatch 闭锁spa

六、Semaphore 信号量.net

注意:四、五、6参见后续章节线程

一、wait和notify/notifyAll 

等待/通知机制,是指一个线程A调用了对象O的wait方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操做。上述两个线程经过对象O完成交互,而对象上的wait()和notify()/notifyAll()的关系就如同开关信号同样,用来完成等待方和通知方之间的交互工做。code

建立两个线程WaitThread和NotifyThread,前者检查flag值是否为false,若是符合要求,进行后续操做,不然在lock上等待,后者在睡眠了一段时间值对lock进行通知,示例代码以下:orm

package com.black.example.helloworld.thread;

import ch.qos.logback.core.util.TimeUtil;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 等待/通知 超时设置模式
 */
public class WaitNotify {

    static boolean flag = true;
    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread waitThread =new Thread(new WaitDemo(),"WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread =new Thread(new NotifyDemo(),"NotifyThread");
        notifyThread.start();

    }

    static class WaitDemo implements Runnable{
        @Override
        public void run() {
            //加锁,拥有lock的monitor
            synchronized (lock){
                //条件不知足时,继续wait,同时释放lock的锁
                while (flag){
                    try {
                        System.out.println(Thread.currentThread()+"flag is true, wait @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //条件知足时,完成工做
                System.out.println(Thread.currentThread() + " flag is false,running @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class NotifyDemo implements Runnable{
        @Override
        public void run() {
            try {
                //加锁,拥有lock对象的Monitor
                synchronized (lock){
                    //获取lock的锁,而后进行通知,通知时不会释放lock的锁
                    //直到当前线程释放了lock后,WaitThread才能从wait方法中返回
                    System.out.println(Thread.currentThread()+" hold lock, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    lock.notifyAll();
                    flag = false;
                    TimeUnit.SECONDS.sleep(5);
                }

                //再次加锁
                synchronized (lock){
                    System.out.println(Thread.currentThread()+" hold lock again, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    TimeUnit.SECONDS.sleep(5);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

}

运行结果以下:

二、await和signal/signalAll 

Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,须要提早获取Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)建立出来的,换句话说,Condition是依赖Lock对象的。

通常都会讲Condition对象做为成员变量。当调用await()方法后,当前线程会是否锁并进入等待,并且其余线程调用signal()方法,通知当前线程后,当前线程才从await()方法返回,而且在返回前已经获取到了锁。

经常使用方法及描述,见下表:

await()  等待  与 singnal()通知的使用,示例以下:

package com.black.example.mutileThread;

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

/**
 * Condition 配合Lock  实现线程的等待 与通知
 * Created by liuzp on 2018/7/24.
 */
public class ConditionDemo {
    public static Lock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                lock.lock();//请求锁
                Thread.currentThread().setName("await-thread-lzp");
                try {
                    System.out.println(Thread.currentThread().getName() + "==》进入等待状态,直到被通知");
                    condition.await();//设置当前线程进入等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();//释放锁
                }
                System.out.println(Thread.currentThread().getName() + "==》继续执行");
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                lock.lock();//请求锁
                Thread.currentThread().setName("signal-thread-lzp");
                try {
                    System.out.println(Thread.currentThread().getName() + "=》进入唤醒一个Condition上的线程");
                    Thread.sleep(2000);//休息2秒
                    condition.signal();//随机唤醒等待队列中的一个线程
                    System.out.println(Thread.currentThread().getName() + "休息结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();//释放锁
                }
            }
        }.start();
    }
}

运行结果:

三、sleep/yield/join

  1. 对于sleep()方法应该很熟悉了,让当前线程睡眠一段时间。期间不会释听任何持有的锁
  2. yield()方法其做用主要是让当前线程从运行状态转变为就绪状态,由线程调度从新选择就绪状态的线程分配CPU资源。至于最终会选取哪一个线程分配CPU资源就由调度策略来决定了,有可能仍是该线程,有可能换为其它线程。
  3. join方法,做用是暂停当前线程,等待被调用线程指向结束以后再继续执行。

使用join的时候须要注意:
  一、调用join的时候,当前线程不会释放掉锁,若是调用线程也须要该锁则就会致使死锁!
  二、join方法不会启动调用线程,因此,在调用join以前,该调用线程必须已经start启动,不然不会达到想要的效果。
join的底层实际是就是使用了一个自旋等待机制,判断调用线程是否死亡,若是没有则一直让当前线程wait。能够看一下底层实现源码:

public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (millis == 0) {
            while (isAlive()) {//若是调用者依旧没有结束,让当前线程进行等待
                wait(0);//注意这里的wait是等待的当前线程,而不是调用者线程
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);//指定等待的时间
                now = System.currentTimeMillis() - base;
            }
        }
    }

 

上一篇:学习笔记四:初识线程

下一篇:学习笔记六:线程间的协做与通讯之并发工具类

相关文章
相关标签/搜索