线程之间的通信
线程之间的通信,其实也就是一个线程获取另外一个或多个处理完任务的线程后所返回来的值,共有以下两种方法实现:
一.主线程等待法
public class CycleWait implements Runnable {
//设置value值,用于返回给被调用线程
private String value;
public void run() {
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cw = new CycleWait();
Thread t = new Thread(cw);
//开始调用子线程
t.start();
// while (cw.value == null){
// Thread.currentThread().sleep(100);
// }
/*join方法,功能与上面被注释的三行代码完全相同,
都是用来判断子线程是否已执行完,如果执行完,则执行下面的
System.out.println("value : " + cw.value)代码,如果没有执行完,
则阻塞当前线程,直到子线程执行完,但是上面三行代码只是休眠0.1秒,
如果过了这个时间子线程依然没有返回值回来也会执行下面代码了*/
t.join();
//获取子线程的返回值
System.out.println("value : " + cw.value);
}
}
二.通过线程池实现
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个线程池,有好多种创建方法,每种意义也不一样,
// 线程池的内容我后面几篇文章再详细讲
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//开始通过线程池的方式调用子线程,子线程的逻辑在自定义类MyCallable类里
Future<String> future = newCachedThreadPool.submit(new MyCallable());
//isDone方法,我上篇文章有讲过,用来判断线程是否执行完成
if(!future.isDone()){
System.out.println("task has not finished, please wait!");
}
try {
//获取子线程返回值
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
newCachedThreadPool.shutdown();
}
}
}
/**
* 自定义类,通过继承Callable类实现多线程
*/
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception{
String value="test";
System.out.println("Ready to work");
Thread.currentThread().sleep(5000);
System.out.println("task done");
return value;
}
}
线程的状态
上面我提到了调用join方法的线程会被阻塞。阻塞,它是线程的六种状态里面的一种,比如在synchronized修饰的临界代码1中,在外面等待进入临界代码的线程释放锁的这些线程的状态,就是阻塞状态。
线程的六种状态:
- 新建(New):创建后尚未启动的线程的状态
- 运行(Runnable):包含Running 和Ready
- 无限期等待(Waiting)不会被分配CPU 执行时间,需要显式被唤
- 限期等待(Timed Waiting ):在一定时间后会由系统自动唤醒
- 阻塞(Blocked) : 等待获取排它锁
- 结束(Terminated): 已终止线程,线程已经结束
特别要注意的是:阻塞状态,比如在synchronized修饰的代码里,在外面等待正在执行临界代码的线程释放锁的这些线程此时就是阻塞状态
可以改变线程状态的方法
sleep和wait
Thread.sleep(ms):sleep(ms) 方法是一个静态方法,用于使当前线程在指定时间内休眠(暂停)。
Thread.wait(),让线程处于等待状态
sleep和wait的区别:
基本差别
- sleep是Thread 类的方法,wait 是Object 类中定义的方法
- sleep() 方法可以在任何地方使用
- wait() 方法只能在synchronized 方法或synchronized块中使用
在这里解释一下第三点:
结合基本差别和本质差别看,因为我只能有了锁以后,执行wait方法我才能去释放锁(相当于让线程处于等待状态),否则没锁也释放则报错
本质差别
- Thread.sleep只会让出CPU,不会导致锁行为的改变(当前线程不会释放锁,其他线程没有机会执行)
- Object.wait不仅让出CPU,还会释放已经占有的同步资源锁(其他线程就可以来执行了,当前线程退位啦)
notify()/notifyAll()
两者都用来唤醒线程,使线程进入锁池,等待执行。
两者的区别
- notifyAll()会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
- notify()只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
yield和 interrupt
两者都是要释放锁
yield()方法:给调度器一个暗示,可以释放锁资源,但是调度器也可能不释放,也可能释放
interrupt() 方法:当我们调用 sleep() 方法时,编译器会要求我们捕获中断异常 InterruptedException,这是因为线程的休眠状态可能会被中断,这是这个方法的主要作用,目的是要将一个被阻塞状态的线程中断。
interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。 更确切的说,如果线程被Object.wait,Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出InterruptedException。
- 临界代码:比如被synchronized修饰的代码称之为临界代码,临界代码满足了原子性,可见性,有序性 ↩︎