java suspend wait_java中线程阻塞之sleep、suspend、wait、resume、notify方法解析

java线程的5状态包括create、runnable、running、blocked、dead。create是指用new线程被创建,但是还没有准备好各种资源。runnable是指使用start启动线程,这时

java线程的5状态包括create、runnable、running、blocked、dead。

create是指用new线程被创建,但是还没有准备好各种资源。

runnable是指使用start启动线程,这时候系统会为线程分配除cpu以外的所有需要的资源。

running是指cpu调度处于runnable状态的线程使其占用cpu时间片真正开始运行。

blocked是指由于某种原因导致running中的线程暂停执行,被放到阻塞队列中,知道导致阻塞的原因被解除。

dead指线程结束,包括正常结束(如执行完run方法)和非正常结束(如抛出异常等)。

那么造成blocked的原因有哪些呢?

(1) 调用 sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。

(2) 用 suspend()暂停了线程的执行。除非线程收到 resume() 消息,否则不会返回“可运行”状态。

(3) 用 wait()暂停了线程的执行。除非线程收到 nofify() 或者 notifyAll()消息,否则不会变成“可运行”(是的,看起来同原因 2 非常相象,但有一个明显区别是我们马上要揭示的)

(4) 线程正在等候一些 IO(输入输出)操作完成。

(5) 线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。

先写两个基类:

class Blockable extends Thread {

private Peeker peeker;

protected int i;

public Blockable(){

peeker = new Peeker(this);

}

public synchronized int read(){ return i;}

public synchronized void update(){

System.out.println(this.getClass().getName()+"state :i = " + i);

}

public void stopPeeker(){

peeker.terminate();

}

}

class Peeker extends Thread {

private Blockable b;

private int session;

private boolean stop = false;

public Peeker(Blockable b){

this.b = b;

start();

}

public void run(){

while (!stop) {

System.out.println(b.getClass().getName()

+ " Peeker " + (++session)

+ "; value = " + b.read());

try {

sleep(1000);

} catch (InterruptedException e){}

}

}

public void terminate() {stop = true;}

}

Blockable 类打算成为本例所有类的一个基础类。一个 Blockable 对象包含了一个i信息。用于显示这些信息的方法叫作 update() 。我们发现它用

getClass.getName() 来产生类名,而不是仅仅把它打印出来;这是由于 update()不知道自己为其调用的那个类的准确名字,因为那个类是从 Blockable 衍生出来的。

在 Blockable 中,变动指示符是一个 int i;衍生类的 run()方法会为其增值。

针对每个 Bloackable 对象,都会启动 Peeker 类的一个线程。Peeker 的任务是调用 read()方法,,检查与自己

关联的 Blockable 对象,看看 i 是否发生了变化,最后打印检查结果。注意 read()和 update() 都是同步的,要求对象的锁定能自由解除,这一点非常重要。

(1)sleep方法的调用引起其他线程阻塞是因为sleep不会释放对象锁。下是测试代码:

class Sleeper1 extends Blockable {

public Sleeper1() {

super();

}

public synchronized void run() {

while(true) {

i++;

update();

try {

sleep(1000);

} catch (InterruptedException e){}

}

}

}

class Sleeper2 extends Blockable {

public Sleeper2() {

super();

}

public void run() {

while(true) {

change();

try {

sleep(1000);

} catch (InterruptedException e){}

}

}

private synchronized void change(){

i++;

update();

}

}

public class SleepBlock {

public static void main(String[] args) {

new Sleeper1().start();

new Sleeper2().start();

}

}我们会发现Sleeper1中的run方法是同步的,由于run方法里面是死循环,当Sleeper1线程启动后,Sleeper1内的Peeker线程根本无法运行。因为Sleeper1的sleep方法并不会释放对象锁。

在 Sleeper1 中,整个 run()方法都是同步的。我们可看到与这个对象关联在一起的 Peeker 可以正常运行,直到我们启动线程为止,随后 Peeker 便会完全停止。这正是“堵塞”的一种形式:因为 Sleeper1.run()是同步的,而且一旦线程启动,它就肯定在 run()内部,方法永远不会放弃对象锁定,造成 Peeker 线程的堵塞。

Sleeper2 通过设置不同步的运行,提供了一种解决方案。只有 change() 方法才是同步的,所以尽管 run()位于 sleep()内部,Peeker 仍然能访问自己需要的同步方法——read()。在这里,我们可看到在启动了Sleeper2 线程以后,Peeker 会持续运行下去。

(2)suspend方法调用引起其他线程阻塞也是因为suspend方法不会释放对象锁,这是不安全的。此方法已经废弃。因为使用suspend会发生一种很愚蠢的现象,就是自己把自己挂起,也就是进入阻塞状态。但是还抢着对象锁(前提是有同步方法访问同样的资源),这样其他线程来访问同步方法也只能傻傻等待。就好像你自己拿着钥匙在那等别人来开门,别人来开你又不给钥匙。

(3)wait方法阻塞自己会释放对象锁,这一点和sleep、suspend是根本区别,因此他不会阻碍其他线程。

若必须等候其他某些条件(从线程外部加以控制)发生变化,同时又不想在线程内一直傻乎乎地等下去,一般就需要用到 wait()。wait()允许我们将线程置入“睡眠”状态,同时又“积极”地等待条件发生改变。而且只有在一个 notify() 或 notifyAll()发生变化的时候,线程才会被唤醒,并检查条件是否有变。因此,我们认为它提供了在线程间进行同步的一种手段。

我们也可以看到 wait()的两种形式。第一种形式采用一个以毫秒为单位的参数,它具有与 sleep()中相同的含义:暂停这一段规定时间。区别在于在 wait()中,对象锁已被解除,而且能够自由地退出 wait(),因为一个 notify() 可强行使时间流逝。第二种形式不采用任何参数,这意味着 wait()会持续执行,直到 notify() 介入为止。而且在一段时间以后,不会自行中止。

(4)IO引起的阻塞。这一部分主要是IO那一块的。由于访问IO可能会等待,因此会引起阻塞。

(5)在上面已经可以看出来了,不同线程在访问相同对象的同步方法时,会引起阻塞。需要等待一方先完成才能进行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值