Java并发编程之线程篇之线程间通信(四)(3)

电脑杂谈  发布时间:2019-08-27 18:03:07  来源:网络整理

通知方依照如下原则:

获得对象的锁改变条件通知所有期待在对象上的线程。

对应伪代码分别如下:

使用synchronized方法:

synchronized(对象){
    改变条件
    对象.notifyAll()
}

线程死锁_linux 线程 死锁_多线程死锁的伪代码

使用lock模式:

    lock.lock();
    try{
        改变条件
        condition.singleAll();
    }finally{
        lock.unlock();
    }

除了使用下面我们介绍的经典模式以外,我们还可以使用Thread.join()方法。join方法的使用意义如下:

当线程A调用线程B对象(bThread)的join方法,其意义是当前线程A等待线程B终止后,才从线程A中bThread.join()代码的调用处返回。线程除了join方法以外还提供了join(long millis)和void join(long millis, int nanos)这两个具备超时特性的方式。这两个方法的含义是即使在给定的时间内线程B没有终止。那么线程A将会从该方式中返回。下面我们来看一下join方法的使用实例,如下所示:

class AThread extends Thread {
    public AThread() {
        super("[AThread]”);
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + "-->start”);
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(threadName + "loop at" + i);
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(threadName + "--->end”);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class BThread extends Thread {
    private AThread mAThread;
    public BThread(AThread aThread) {
        super("[BThread]”);
        this.mAThread = aThread;
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + "-->start”);
        try {
            mAThread.join();//使B线程等待,需要等待A线程执行完毕后,才能继续执行
            System.out.println(threadName + "--->end”);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "-->start”);
        AThread aThread = new AThread();
        BThread bThread = new BThread(aThread);
        try {
            aThread.start();
            TimeUnit.SECONDS.sleep(1);
            bThread.start();
            aThread.join();//主线程等待A线程执行完毕后,才继续执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "--->end”);
    }
}

在上述事例中,我们主要推动下面两个效果:

我们查看输出结果:

main-->start      //main线程启动
[AThread]-->start //A线程启动
[AThread]loop at0 //A线程开始执行循环
[AThread]loop at1
[BThread]-->start //B线程开始启动,因为在B线程中调用了aThread.join()那么B线程会等待A线程执行完毕后,才开始执行
[AThread]loop at2 //A线程继续执行
[AThread]loop at3
[AThread]loop at4
[AThread]--->end //A线程执行完毕后,
[BThread]--->end //A线程执行完毕后,唤醒B线程继续执行
main--->end //主线程执行完毕

整个程序是根据我们之前的逻辑在运行,下面我们来查看线程中join方法的实现机理,具体代码如下所示:

join()方法内部会调用join(final long millis)方法。

    //同步方法默认的锁为调用该方法的对象,也就是xxThread.join()的xxThread
    public final synchronized void join(final long millis)
    throws InterruptedException {
        if (millis > 0) {//如果等待时间大于0
            if (isAlive()) {
                final long startTime = System.nanoTime();
                long delay = millis;
                do {
                    wait(delay);
                } while (isAlive() && (delay = millis -
                        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
            }
        } else if (millis == 0) {//如果等待时间为0,
            while (isAlive()) {
                wait(0);//当前线程存活,那么会使当前线程等待(当前线程指运行xxThread.join的线程,而不是xxThread)
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative”);
        }
    }

我们简单的剖析一下代码,当B线程读取A线程的join()方法时,当前锁对象为A线程。在join()方法内部会调用wait(0),该方式会使B线程等待。只有当A线程执行完毕后,也就是A线程中止后。才会激活B线程。

线程执行完毕或轮询终止时,会读取线程自身的notifyAll()方法,会通知所有期待在该线程对象的线程。

在上述文章中,我们都是讲解的多个线程之前的通信,那么在同一线程中,在某个时刻我们想获得线程中修改的数组,我们可以借助ThreadLocal,在之前的文章中Android-Handler机制之ThreadLocal,我们介绍过ThreadLocal的使用。下面我们借助一个例子来认识ThreadLocal的使用。具体实例如下:

class ThreadLocalTest {
    private static ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        mThreadLocal.set("线程main”);
        new Thread(new A()).start();
        new Thread(new B()).start();
        System.out.println(mThreadLocal.get());
    }
    static class A implements Runnable {
        @Override
        public void run() {
            mThreadLocal.set("线程A”);
            System.out.println(mThreadLocal.get());
        }
    }
    static class B implements Runnable {
        @Override
        public void run() {
            mThreadLocal.set("线程B”);
            System.out.println(mThreadLocal.get());
        }
    }
}

输出结果:

main
线程A
线程B

这里就不再介绍ThreadLocal的原理了,有兴趣的小伙伴可以查看Android-Handler机制之ThreadLocal文章进行理解。

这里提供一个线程交替打印素数偶数的举例,来帮助你们巩固所学的知识点。有兴趣的小伙伴,可以查看项目PrintOddEventNumber。

该文章参考下面图书,站在巨人的大腿上。可以看得更远。


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-120751-3.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...