线程间的交互和通信
-
一个线程启动另一个线程
public static void main(String[] args){ new Thread().start(); }
主线程启动另一个线程。
-
一个线程终结另个一线程
A.Thread.stop()
来停止一个线程,但是已经被弃用了,因为stop
结果是不可预期的,它是直接将线程干掉了,即是你的业务刚执行到一半,这样就导致结果是不可预期的,比较危险。Thread thread = new Thread(); thread.start(); thread.stop();
B.
Thread.interrupt()
来中断一个线程的执行。它的原理是调用interrupt
方法后,将该线程编辑为中断状态,需要我们自己处理这个状态。Thread thread = new Thread(){ @Override public void run(){ if(isInterrupted()){ //do something } } }; thread.start(); thread.interrupt();
判断一个线程是否标记为中断状态,可以使用
thread.isInterrupted()
,也可以使用Thead
的静态方法Thread.interruped()
,二者的区别是前者可以重复多次使用,后者只能使用一次,因为使用之后会将标记复位,即置为false
,实际使用根据自己具体的逻辑来具体使用哪种方式。C. 当一个线程正在休眠中,此时被中断,会怎样?
答案是会立即抛出InterruptedException
,然后线程会被立即结束,但是中断标志一直是false
。因为当处于休眠状态,说明这个线程目前没有在处理事情,也就是说可以立即打断,也不会产生影响,所以会被立即终止。这也是当我们写Thread.sleep(10);
的时候要求强制捕获InterruptedException
的原因,所以如果我们有需要则要在异常中处理中断的后续逻辑。 -
两个线程互相配合
需要使用wait()
和notify()
以及notifyAll()
,这里有必要说明下,这3个方法是针对monitor
来说的,并不是针对线程,需要等的是monitor
,需要唤醒的也是monitor
,和线程无关,而且这3个方法也必须是在同步代码块中才可以使用。
下面我们来简单说一下wait
和notify
的工作模型。
工作模型
首先我们看下这段代码,有两个同步方法methodA
和methodB
,他们的锁对象都是this
,即monitor
是同一个,下面来说明下这个工作模型。
当ThreadA
先访问methodB
时,它先来到等待访问排队区,发现里面没人,就他自己,于是它从monitor
那里拿到了锁,此时的线程正在访问区里面放的是ThreadA
。但是methodB
里面的代码是wait()
,此时monitor
会将ThreadA
放到右面的等待唤醒区,同时ThreadA
所持有的锁也会被释放,正在访问区是空的。这时ThreadB
来了,想访问methodA
,它问了下monitor
,我可以访问methodA
吗,因为此时的ThreadA
的锁已经释放了,它理所当然的可以访问methodA
,此时的线程正在访问区中里面放的是ThreadB
, ThreadB
拿着锁来到了methodA
内部,此时它发现里面是notifyAll
,所以此时的monitor
就将等待唤醒区里面的所有等待唤醒的线程都唤醒了,然后ThreadB
就是放了锁,然后骂骂咧咧的走了,此时线程正在访问区是空的。等待唤醒区中的ThreadA
被唤醒,他只能再次来到等待访问排队区,不能插队到正在访问区中,只能公平的再次去竞争锁,然后发下排队的就他自己,于是它又从monitor
那里拿到了锁,然后就去访问methodB
了,但是里面又是wait()
,于是乎它又释放锁,然后又进入了等待唤醒区,可以没有人再去访问methodA
来告诉monitor
区唤醒唤醒等待区里面的线程了,此时ThreadA
将永远在等待唤醒区等一辈子。
大致的一个工作模型就是这样的,可能描述的不太清楚,但是大致原理是清晰的,相信多看几遍总会明白的,哈哈哈。。。
这里为什么调用的是nitofyAll
呢,而不是notify
呢?因为针对这个monitor
而言,有可能等待唤醒区有好多个线程正在等待,而不是一个,如果只唤醒一个,那剩下的咋办?所以只能一次性唤醒所有的,除非你明确知道等待唤醒区中只有一个线程在等待被唤醒。
所以从这个工作模型来看,wait
,notify
以及notifyAll
都是针对monitor
而言的,是monitor
让一个线程区等待唤醒区,也是monitor
来进行唤醒等待唤醒区里面的线程,而且wait
和notify
一定是成对出现的。
下面举一个简单的栗子。
public class Demo {
public static void main(String[] args) {
new WaitNotifyTest().runTest();
}
private static class WaitNotifyTest{
private int x;
void runTest() {
Thread threadA = new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
plus();
}
};
threadA.start();
Thread threadB = new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
printX();
}
};
threadB.start();
}
private synchronized void plus() {
x++;
notifyAll();
}
private synchronized void printX() {
if (x == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("The x is: " + x);
}
}
}
执行结果如下:

- 插队
将一个线程插到当前线程之前执行,插队的方法就是join
。
简单代码如下:public static void main(String[] args) { Thread threadA = new Thread(){ @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("run method in ThreadA class is finished。"); } }; threadA.start(); try { threadA.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main method is finished。"); }
上述代码执行的结果:
执行结果
当ThreadA
执行了join
插队方法后,main
方法所在的线程会等ThradA
线程执行结果后在执行。
join
可以说是简化后的wait
,notify
,只是不需要我们调用notify
。
它的作用时将并行的两个线程变成串行化。
-
yield
。暂时让出时间片,让给同优先级的线程,这个时间很短很短,让出之后马上会再次获取到时间片。
由于个人能力有限,如有错误之处,还望指出,我会第一时间验证并修改。
理解事物,看本质。共勉。
网友评论