文章目录
首先了解Thread的API是如何改变线程的状态的假死现象解决
wait条件改变异常
首先了解Thread的API是如何改变线程的状态的
1)新创建一个新的线程对象后,再调用它的start()方法,系统会为此线程分配CPU资源,使其处于Runnable(可运行)状态,这是一个准备运行的阶段。如果线程抢占到CPU资源,此线程就处于Running(运行)状态。2)Runnable状态和Running状态可相互切换,因为有可能线程运行一段时间后,有其他高优先级的线程抢占了CPU资源,这时此线程就从Running状态变成Runnable状态。线程进入Runnable状态大体分为如下5种情况: -> 调用sleep()方法后经过的时间超过了指定的休眠时间。 ->线程调用的阻塞IO已经返回,阻塞方法执行完毕。 ->线程成功地获得了试图同步的监视器。 ->线程正在等待某个通知,其他线程发出了通知。 ->处于挂起状态的线程调用了resume恢复方法。3 )Blocked是阻塞的意思,例如遇到了一个IO操作,此时CPU处于空闲状态,可能会转而把CPU时间片分配给其他线程,这时也可以称为“暂停”状态。Blocked状态结束后,进人Runnable状态,等待系统重新分配资源。出现阻塞的情况大体分为如下5种: ->线程调用sleep方法,主动放弃占用的处理器资源。 ->线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞。 ->线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。 ->线程等待某个通知。 ->程序调用了suspend方法将该线程挂起。此方法容易导致死锁,尽量避免使用该方法。4 ) run()方法运行结束后进入销毁阶段,整个线程执行完毕。 每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU的调度;反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。
假死现象
“假死”的现象其实就是线程进入 WAITING等待状态。如果全部线程都进人WAITING状态,则程序就不再执行任何业务功能了,整个项目呈停止状态。这在使用生产者与消费者模式时经常遇到。服务类
public class ServiceResources {
private List
<String> list
= new ArrayList<>();
synchronized public void push() throws InterruptedException
{
while (list
.size() > 0) {
System
.out
.println(Thread
.currentThread().getName() + " 生产者进入睡眠");
this.wait();
}
System
.out
.println(Thread
.currentThread().getName() + " 生产");
list
.add("anything");
this.notify();
}
synchronized public void take() throws InterruptedException
{
while (list
.size() == 0) {
System
.out
.println(Thread
.currentThread().getName() + " 消费者进入睡眠");
this.wait();
}
System
.out
.println(Thread
.currentThread().getName() + " 消费");
list
.remove(0);
this.notify();
}
}
多线程
public class ThreadP extends Thread {
private ServiceResources resources
;
public ThreadP(ServiceResources resources
, String name
) {
this.resources
= resources
;
this.setName(name
);
}
@Override
public void run() {
while (true) {
try {
resources
.push();
} catch (InterruptedException e
) {
break;
}
}
}
}
public class ThreadC extends Thread {
private ServiceResources resources
;
public ThreadC(ServiceResources resources
, String name
) {
this.resources
= resources
;
this.setName(name
);
}
@Override
public void run() {
while (true) {
try {
resources
.take();
} catch (InterruptedException e
) {
break;
}
}
}
}
运行类
public class Run {
public static void main(String
[] args
) throws InterruptedException
{
ServiceResources resources
= new ServiceResources();
ThreadP threadP1
= new ThreadP(resources
, "P1");
ThreadP threadP2
= new ThreadP(resources
, "P2");
ThreadC threadC1
= new ThreadC(resources
, "C1");
ThreadC threadC2
= new ThreadC(resources
, "C2");
threadP1
.start();
threadP2
.start();
threadC1
.start();
threadC2
.start();
Thread
.sleep(5000);
threadP1
.interrupt();
threadP2
.interrupt();
threadC1
.interrupt();
threadC2
.interrupt();
Thread
[] threadArray
= new Thread[Thread
.currentThread().getThreadGroup().activeCount()];
Thread
.currentThread().getThreadGroup().enumerate(threadArray
);
for (int i
= 0; i
< threadArray
.length
; i
++) {
System
.out
.println(threadArray
[i
].getName() + " " + threadArray
[i
].getState());
}
}
}
运行结果 从打印的信息来看,呈假死状态的进程中所有的线程都呈WAITING状态。为什么会出现这样的情况呢?在代码中已经用了wait/notify啊?在代码中确实已经通过wait/notify进行通信了,但不保证notify唤醒的是异类,也许是同类,比如“生产者”唤醒“生产者”,或“消费者”唤醒“消费者”这样的情况。如果按这样情况运行的比率积少成多,就会导致所有的线程都不能继续运行下去,大家都在等待,都呈WAITING状态,程序最后也就呈“假死”状态,不能继续运行下去了。
解决
解决“假死”的情况很简单,将P.java和C.java文件中的notifyO改成notifyAll0)方法即可,它的原理就是不光通知同类线程,也包括异类。这样就不至于出现假死的状态了,程序会一直运行下去。
wait条件改变异常
这个问题多见与少生产多消费中, 简单来说就是生产赶不上消费, 导致在消费的时候发生异常服务类
public class ServiceResources {
private List
<String> list
= new ArrayList<>();
synchronized public void push() throws InterruptedException
{
if (list
.size() > 0) {
System
.out
.println(Thread
.currentThread().getName() + " 生产者进入睡眠");
this.wait();
}
System
.out
.println(Thread
.currentThread().getName() + " 生产");
list
.add("anything");
this.notifyAll();
}
synchronized public void take() throws InterruptedException
{
if (list
.size() == 0) {
System
.out
.println(Thread
.currentThread().getName() + " 消费者进入睡眠");
this.wait();
}
System
.out
.println(Thread
.currentThread().getName() + " 消费");
list
.remove(0);
this.notifyAll();
}
}
运行类
public class Run {
public static void main(String
[] args
) throws InterruptedException
{
ServiceResources resources
= new ServiceResources();
ThreadP threadP1
= new ThreadP(resources
, "P1");
ThreadC threadC1
= new ThreadC(resources
, "C1");
ThreadC threadC2
= new ThreadC(resources
, "C2");
ThreadC threadC3
= new ThreadC(resources
, "C3");
ThreadC threadC4
= new ThreadC(resources
, "C4");
ThreadC threadC5
= new ThreadC(resources
, "C5");
threadP1
.start();
threadC1
.start();
threadC2
.start();
threadC3
.start();
threadC4
.start();
threadC5
.start();
Thread
.sleep(5000);
Thread
[] threadArray
= new Thread[Thread
.currentThread().getThreadGroup().activeCount()];
Thread
.currentThread().getThreadGroup().enumerate(threadArray
);
for (int i
= 0; i
< threadArray
.length
; i
++) {
System
.out
.println(threadArray
[i
].getName() + " " + threadArray
[i
].getState());
}
}
}
比问题的出现就是因为在服务类中使用了if语句作为条件判断,代码如下:
if (list
.size() == 0) {
System
.out
.println(Thread
.currentThread().getName() + " 消费者进入睡眠");
this.wait();
}
因为条件发生改变时并没有得到及时的响应,所以多个呈wait状态的线程被唤醒,继而执行list.remove(0)代码而出现异常。解决这个办法是,将if改成while语句即可。