【多线程】线程通信之join、ThreadLocal

一、方法join

       在很多情况下,主线程创建并启动子线程,如果子线程中需要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。如果主线程想等待子线程执行完成之后在结束,比如子线程执行一个方法,主线程要取得这个方法的返回值,就要用到join()方法了。方法join的作用是等待线程对象销毁。线程Thread除了提供join方法之外,还提供了join(long  millis)和join(long millis,int nanos)两个具有超时特性的方法。这两个超时方法表示,如果线程thread在给定的超时时间内没有销毁,那么将从该超时方法中返回。

1、简单demo

      

public class MyThread extends Thread {

	@Override
	public void run(){
		try {
			int secondValue=(int)(Math.random()*10000);
			System.out.println(secondValue);
			Thread.sleep(secondValue);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		try {
		MyThread threadTest=new MyThread();
		threadTest.start();
		//Thread.sleep(?)
		threadTest.join();
		System.out.println("我想当threadTest对象执行完毕后我再执行,我做到了");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
     输出结果如下:

                

       方法join的作用是使所属的线程对象x正常执行run方法中的任务,而使当前线程z进入阻塞状态,等待线程x销毁后再继续执行线程z后面的代码。

       方法join具有使线程排队运行的作用,有些类似同步的效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。

2、使线程按顺序执行

      之前说过线程调用具有“无序性”的特点,那么  有三个线程T1,T2、T3,怎么确保它们按顺序执行?

   

public class Join {
	public static void main(String[] args) {
		Thread previous=Thread.currentThread();
		for (int i = 0; i < 10; i++) {
			//每个线程拥有前一个线程的引用,需要等待前一个线程终止,才能从等待中返回
			Thread thread=new Thread(new Domino(previous),String.valueOf(i));
			thread.start();
			previous=thread;
		}
	}
	//静态内部类
	static class Domino implements Runnable{
		private Thread thread;
		public Domino(Thread thread){
			this.thread=thread;
		}
		public void run(){
			try {
				thread.join();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+" terminate.");
		}
	}

}
     

       上述代码,创建类10个线程,编号0~9,每个线程调用前一个线程的join方法,也就是线程0结束了,线程1才能从join方法中返回,而线程0需要等待main线程结束。每个线程终止的前提是前驱线程的终止,每个线程等待前驱线程终止后,才从join方法返回,本质上仍是等待/通知机制(等待前驱线程结束,结束前驱线程结束通知)

        join方法源码如下:

        

 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);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

二、ThreadLocal

       之前【多线程】volatile关键字中提及了内存模型具有原子性可见性有序性

       Thread顾名思义可以理解为线程本地变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。换句话说就是每个线程绑定自己的值。每个线程往这个ThreadLocal中读写是线程隔离的,互相之间不会影响

        通过set(T)方法来设置一个值,在当前线程下通过get方法获取到原先设定的值。

        

public class Run {
	public static ThreadLocal t1=new ThreadLocal();
	public static void main(String[] args) {
		if (t1.get()==null) {
			System.out.println("从未放过值");
			t1.set(" 我的值 ");
		}
		System.out.println(t1.get());
	}

}
     接下来验证线程的隔离性。

     

public class Tools {
	/**
	 * 验证线程变量的隔离性
	 */
	public static ThreadLocal t1=new ThreadLocal();
}
    
public class ThreadA extends Thread {
	@Override
	public void run(){
		try {
			for (int i = 0; i < 100; i++) {
				Tools.t1.set("ThreadA"+(i+1));
				System.out.println("ThreadA get Value="+Tools.t1.get());
				Thread.sleep(200);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

}
     
public class ThreadB extends Thread {
	@Override
	public void run(){
		try {
			for (int i = 0; i < 100; i++) {
				Tools.t1.set("ThreadB"+(i+1));
				System.out.println("ThreadB get Value="+Tools.t1.get());
				Thread.sleep(200);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

}
     
public class Run {
	public static void main(String[] args) {
		try {
			ThreadA a=new ThreadA();
			ThreadB b=new ThreadB();
			a.start();
			b.start();
			for (int i = 0; i < 100; i++) {
				Tools.t1.set("Main"+(i+1));
				System.out.println("Main get Value="+Tools.t1.get());
				Thread.sleep(200);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
     

     虽然3个线程都向t1对象中set数据值,但每个线程都能取值自己对应的数据。(t1对象是单例的)

     ThreadLocal和synchronized都能保证线程安全,synchronized是用时间换空间,synchronized是用空间换时间。


      

        

         
  

     



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值