• JAVA并发-join


    概念

    join方法,一种特殊的wait,当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到调用join方法的线程结束,再继续执行。

    一般情况下,都是主线程创建一个子线程,子线程调用join方法,主线程会进入阻塞状态,直到子线程运行结束。

    简单案例

    public class JoinThreadDemo {
    
    	public static void main(String[] args) {
    		JoinRunnable runnable1 = new JoinRunnable();
    		Thread thread1 = new Thread(runnable1, "线程1");
    		System.out.println("主线程开始执行!");
    		thread1.start();
    //		try {
    //			thread1.join();
    //		} catch (InterruptedException e) {
    //			e.printStackTrace();
    //		}
    
    		System.out.println("主线程执行结束!");
    	}
    
    	static final class JoinRunnable implements Runnable {
    		@Override
    		public void run() {
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "开始执行!");
    
    			for (int i = 1; i <= 5; i++) {
    				System.out.println(name + "执行了[" + i + "]次");
    			}
    		}
    	}
    }
    
    

    Output:

    主线程开始执行!
    主线程执行结束!
    线程1开始执行!
    线程1执行了[1]次
    线程1执行了[2]次
    线程1执行了[3]次
    线程1执行了[4]次
    线程1执行了[5]次
    

    取消注释,调用join方法:

    主线程开始执行!
    线程1开始执行!
    线程1执行了[1]次
    线程1执行了[2]次
    线程1执行了[3]次
    线程1执行了[4]次
    线程1执行了[5]次
    主线程执行结束!
    

    替代join案例

    public class JoinThreadDemo {
    
    	public static void main(String[] args) {
    		JoinRunnable runnable1 = new JoinRunnable();
    		Thread thread1 = new Thread(runnable1, "线程1");
    		System.out.println("主线程开始执行!");
    		thread1.start();
    		try {
    			synchronized (thread1) {
    				while (thread1.isAlive()) {
    					System.out.println("begin wait");
    					//主线程持有thread1对象锁,阻塞,一直到thread1运行结束,jvm唤醒
    					thread1.wait(0);
    					System.out.println("thread wait");
    				}
    			}
    
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    		System.out.println("主线程执行结束!");
    
    	}
    
    	static final class JoinRunnable implements Runnable {
    
    		@Override
    		public void run() {
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "开始执行!");
    
    			for (int i = 1; i <= 5; i++) {
    				System.out.println(name + "执行了[" + i + "]次");
    			}
    		}
    	}
    }
    

    Output:

    主线程开始执行!
    begin wait
    线程1开始执行!
    线程1执行了[1]次
    线程1执行了[2]次
    线程1执行了[3]次
    线程1执行了[4]次
    线程1执行了[5]次
    thread wait
    主线程执行结束!
    

    thread1调用wait后,主线程阻塞,一直到子线程thread1运行结束退出以后,jvm会自动唤醒阻塞在thread1对象上的线程

    那么有没有可能不使用thread1对象阻塞呢?

    下面就是不使用thread1对象阻塞主线程的案例

    替代join案例2

    public class JoinThreadDemo {
    
    	public static void main(String[] args) {
    		Object lock = new Object();
    		Thread thread1 = new Thread(() -> {
    			String name = Thread.currentThread().getName();
    			System.out.println(name + "开始执行!");
    
    			for (int i = 1; i <= 5; i++) {
    				System.out.println(name + "执行了[" + i + "]次");
    			}
    		}, "线程1");
    		System.out.println("主线程开始执行!");
    		thread1.start();
    
    		//thread2自旋唤醒阻塞在lock对象上的主线程
    		Thread thread2 = new Thread(new Thread() {
    			@Override
    			public void run() {
    				while (!thread1.isAlive() && !Thread.currentThread().isInterrupted()) {
    					synchronized (lock) {
    						System.out.println("enter");
    						lock.notifyAll();
    						System.out.println("exit");
    					}
    				}
    			}
    		}, "线程2");
    		thread2.start();
    
    		try {
    			synchronized (lock) {
    				while (thread1.isAlive()) {
    					System.out.println("bb");
    					lock.wait();
    					//停止thread2自旋
    					thread2.interrupt();
    					System.out.println("tt");
    				}
    			}
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println("主线程执行结束!");
    	}
    }
    

    Output:

    主线程开始执行!
    bb
    线程1开始执行!
    线程1执行了[1]次
    线程1执行了[2]次
    线程1执行了[3]次
    线程1执行了[4]次
    线程1执行了[5]次
    enter
    exit
    enter
    exit
    tt
    主线程执行结束!
    

    这里添加了一个线程thread2用于专门自旋唤醒主线程

    用于替换案例一中的,thread1线程结束后,jvm唤醒主线程操作

    join原理

    阻塞主线程

    Thread类中,join源码:

    //Thread类中
    public final void join() throws InterruptedException {
        join(0);
    }
    
    
    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) {    //这个分支是无限期等待直到b线程结束
            while (isAlive()) {
                wait(0);
            }
        } else {    //这个分支是等待固定时间,如果b没结束,那么就不等待了。
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
    

    join成员方法中有synchronized,锁定的对象为调用该方法的对象,即子线程thread1,即主线程持有了thread1对象锁

    可以自己调试,发现进入到join源码的线程为主线程

    唤醒主线程

    子线程thread1执行完毕的时候,jvm会自动唤醒阻塞在thread1对象上的线程,在我们的例子中也就是主线程。至此,thread1线程对象被notifyall了,那么主线程也就能继续跑下去了

    参考

    Thread类中的join()方法原理

    Java Thread的join() 之刨根问底

    Java多线程初探——yield()方法与join()方法

    sleep、yield、wait、join的区别(阿里)

    who and when notify the thread.wait() when thread.join() is called?

  • 相关阅读:
    Daily Scrum NO.4
    Daily Scrum NO.3
    Daily Scrum NO.2
    Daily Scrum NO.1
    Beta任务项录入
    M1事后分析报告
    wenbao与概率
    wenbao与组合数
    wenbao与高斯消元
    wenbao与链表
  • 原文地址:https://www.cnblogs.com/hongdada/p/11739311.html
Copyright © 2020-2023  润新知