线程之间的通信(二)

简介: 线程之间的通信(二)

五 . 方法join#


1. 简单使用#


很多情况下,主线程中启动子线程,然后两条线程并行运行,主线程往往早于子线程之前结束,那么,假如说主线程想等子线程执行完毕后,拿到子线程的结果后再结束,那么最直接的方法就是 使用join()


public class myjoin extends Thread{
    public void run(){
        System.out.println("我是子线程,我要睡两秒...");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        try {
            myjoin myjoin = new myjoin();
            myjoin.start();
            myjoin.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"我成功的等待子线程执行完后,执行");
    }


运行结果


我是子线程,我要睡两秒...
main我成功的等待子线程执行完后,执行


  • join方法的作用就是,执行当前线程的run()中的任务,直到任务结束,线程对象销毁后,才执行后面的代码


2. join方法与异常#


  • 当join()所在的线程碰到了interrupted()会摩擦出怎样的火花呢?

观看如下代码


public class ThreadA extends Thread  {
public void run(){
    try {
        System.out.println("ThreadA 执行了,紧接着睡五秒...");
        Thread.sleep(5000);
        System.out.println("五秒了, ThreadA 醒过来...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}
public class ThreadB extends Thread{
public void run(){
        try{
            System.out.println("ThreadB 启动了...");
            ThreadA threadA = new ThreadA();
            threadA.start();
            threadA.join();
            System.out.println("ThreadA join 之后的代码...");
        }catch (Exception e){
            System.out.println("ThreadB catch块打印了...");
            e.printStackTrace();
        }
}
}
public class ThreadC extends Thread {
Thread thread;
public ThreadC(Thread b){
    this.thread=b;
}
public void run(){
 thread.interrupt();
}
public static void main(String[] args) {
    ThreadB threadB = new ThreadB();
    threadB.start();
    ThreadC threadC = new ThreadC(threadB);
    threadC.start();
    System.out.println("主线程结束...");
}
}


运行结果:


ThreadB 启动了...
主线程结束...
java.lang.InterruptedException
ThreadB 启动了...
ThreadB catch块打印了...
ThreadA 执行了,紧接着睡五秒...
  at java.lang.Object.wait(Native Method)
  at java.lang.Thread.join(Thread.java:1252)
  at java.lang.Thread.join(Thread.java:1326)
  at com.atGongDa.MultiThreading.线程之间的通信.ThreadB.run(ThreadB.java:10)
五秒了, ThreadA 醒过来...


  • 通过出现了中断异常,原因是ThreadA还在运行,并且没出现异常
  • System.out.println("ThreadA join 之后的代码..."); 并未输出, ThreadB确实被中断了
  • ThreadA正常执行结束


当时对join()阻塞的是哪条线程的代码还是有点模糊,现在分析结果,可以看到,join阻塞的是join()方法 所在的那条线程,根据上面的例子,join只能阻塞ThreadB,却不能阻塞ThreadC 和 主线程


3. 意外: join()后面的代码提前执行现象与解释#


运行下面的代码:


public class ThreadQ extends Thread{
    private Thread W;
    public ThreadQ(Thread thread) {
        this.W = thread;
    }
    public void run(){
        synchronized (W){
            System.out.println("ThreadQ准备开始睡三秒..."+System.currentTimeMillis());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadQ...睡醒了..."+System.currentTimeMillis());
        }
    }
}
public class ThreadW extends Thread {
synchronized public void run() {
   try {
       System.out.println("ThreadW 启动了...要睡三秒"+System.currentTimeMillis());
       Thread.sleep(3000);
       System.out.println("ThreadW睡醒了..."+System.currentTimeMillis());
       } catch (InterruptedException e) {
       e.printStackTrace();
   }
}
}
public class ThreadE    {
public static void main(String[] args) {
    ThreadW w = new ThreadW();
    ThreadQ threadQ = new ThreadQ(w);
    w.start();
    threadQ.start();
    try {
        threadQ.join(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("main end...");
}
}


运行结果: 可以看到,main end提前打印出来了


ThreadW 启动了...要睡三秒1549362922247
main end...
ThreadW睡醒了...1549362925248
ThreadQ准备开始睡三秒...1549362925248
ThreadQ...睡醒了...1549362928248


当我们把join(2000)注释掉后结果如下


main end...1549363120715
ThreadQ准备开始睡三秒...1549363120715
ThreadQ...睡醒了...1549363123715
ThreadW 启动了...要睡三秒1549363123715
ThreadW睡醒了...1549363126716


分析结果,不难看出,在线程的启动一条新的线程比它运行自己的代码要快的多.因此,大多数情况下,都是join()方法先执行,拿到对象锁,然后马上释放掉...,然后Q抢到ThreadW对象锁,睡上三秒且不释放,ThreadW因为没有锁,故执行不了自己加上了synchronize的run()方法,本来join()可以阻塞Zhu后面的代码,可是join(2000),发现自己已经过期了,因此ThreadW和ThreadMain就会异步执行


六. join() & sleep()的区别#


  • join()方法底层是wait()实现的,这也就意味着,当我们调用join()方法时,它会做两件事
  • 阻塞join()方法所在的线程1,执行调用join()方法的线程2的run任务
  • 释放掉调用join方法的线程2对象的 对象锁


这也就意味着,其他的线程可以访问调用join方法的线程2的同步方法...

  • sleep()不会释放,运行当前代码的线程对象的 对象锁,也就是说,其他线程是不能访问此线程的其他同步方法的


七 ThreadLocal#


变量值的共享可以使用public static 修饰,所有的线程都使用同一个public static 的变量,如果想实现每一个线程都有自己的共享变量呢? ThreadLocal ,可以把它当成一个专属于当前线程对象的盒子,它保证了线程之前的隔离性


  • 当我们在run方法中,直接new ThreadLocal对象的时候,get()出来的默认值为null

重写 initialValue值设置默认值


@Override
protected Object initialValue() {
    return "123";
}


八 InheritableThreadLocal#


  • 共用一套工具InheritableThreadLocal,实现了让子线程从父线程中获取值,

重写childValue方法,实现继承值的修改,


@Override
protected Object childValue() {
    return "XXX";
}


注意:

当子线程从父线程中获取值的同时,父线程把值修改了,子线程获取到的值为旧值


在java jdk1.5开始,java平台提供了更高级的并发工具,他可以完成以前必须在wait()和notify()上手写代码来完成的各项工作,这在一定程度上让我们几乎没有任何理由再去使用wait和notify,这也是<>提及的第69条,并发工具1.Executor Framework 2,Concurrent Collection 3同步器 Synchronizer ,优先于wait notify



参考书籍<<java多线程编程核心技术>>高洪岩著 <>

相关文章
|
6月前
|
Java
实现Java多线程中的线程间通信
实现Java多线程中的线程间通信
|
3月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
44 1
[Java]线程生命周期与线程通信
|
2月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
46 3
|
7月前
|
Java 程序员
从菜鸟到大神:JAVA多线程通信的wait()、notify()、notifyAll()之旅
【6月更文挑战第21天】Java多线程核心在于wait(), notify(), notifyAll(),它们用于线程间通信与同步,确保数据一致性。wait()让线程释放锁并等待,notify()唤醒一个等待线程,notifyAll()唤醒所有线程。这些方法在解决生产者-消费者问题等场景中扮演关键角色,是程序员从新手到专家进阶的必经之路。通过学习和实践,每个程序员都能在多线程编程的挑战中成长。
67 6
|
3月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
28 1
|
3月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
62 1
|
3月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
54 1
|
3月前
|
Java
|
7月前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
124 0
|
3月前
多线程通信和同步的方式有哪些?
【10月更文挑战第6天】
161 0