线程之间的通信(二)

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

五 . 方法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多线程编程核心技术>>高洪岩著 <>

相关文章
|
11天前
|
Java 程序员
从菜鸟到大神:JAVA多线程通信的wait()、notify()、notifyAll()之旅
【6月更文挑战第21天】Java多线程核心在于wait(), notify(), notifyAll(),它们用于线程间通信与同步,确保数据一致性。wait()让线程释放锁并等待,notify()唤醒一个等待线程,notifyAll()唤醒所有线程。这些方法在解决生产者-消费者问题等场景中扮演关键角色,是程序员从新手到专家进阶的必经之路。通过学习和实践,每个程序员都能在多线程编程的挑战中成长。
|
7天前
|
Java 机器人 程序员
Java中的线程通信:wait、notify与Condition详解
Java中的线程通信:wait、notify与Condition详解
|
11天前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
|
12天前
|
安全 Java
JAVA多线程通信新解:wait()、notify()、notifyAll()的实用技巧
【6月更文挑战第20天】Java多线程中,`wait()`, `notify()`和`notifyAll()`用于线程通信。在生产者-消费者模型示例中,它们确保线程同步。`synchronized`保证安全,`wait()`在循环内防止虚假唤醒,`notifyAll()`避免唤醒单一线程问题。关键技巧包括:循环内调用`wait()`,优先使用`notifyAll()`以保证可靠性,以及确保线程安全和正确处理`InterruptedException`。
|
2天前
|
Java 开发者
线程通信的方法和实现技巧详解
线程通信的方法和实现技巧详解
|
7天前
|
Java
java线程之定制化通信(多轮顺序打印问题)
java线程之定制化通信(多轮顺序打印问题)
9 1
|
3天前
|
消息中间件 Java 数据安全/隐私保护
线程间通信的方法与比较分析
线程间通信的方法与比较分析
|
5天前
|
安全 Java Python
线程间通信详解
线程间通信详解
|
12天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
【6月更文挑战第20天】Java多线程中,wait()和notify()是关键的通信工具,用于解决并发访问共享资源时的数据一致性。这些方法让线程能等待特定条件并高效唤醒,避免忙等待和响应延迟。例如,在生产者-消费者模型中,它们协调生产者和消费者的活动,当队列满或空时,线程调用wait()休眠,notifyAll()唤醒等待的线程,保证同步和资源有效利用。
|
12天前
|
Java
那些年,我们一起学过的JAVA多线程通信
【6月更文挑战第20天】JAVA多线程通信,如同编程旅途中的一道风景,引导我们探索复杂与魅力。wait()是优雅的暂停,释放锁让其他线程继续;notify()唤醒一个,像老师点名,而notifyAll()唤醒全部。生产者-消费者模型中,wait()与notify()协调线程,教会我们合作与解决问题。那些并肩学习的日子,成为永恒的记忆。