sleep()方法和wait()方法问题

选择题:


关于sleep和wait方法,下面描述错误的是( )

A.sleep是线程类Thread的方法,wait是Object的方法。

B.sleep不释放对象锁,wait放弃对象锁。

C.sleep暂停线程,但监控状态仍然保持,结束后会自动恢复。

D.wait后进入等待锁定池,只有针对此对象发出notify方法后获得对象锁进入运行状态。


分析:

A选项毋庸置疑肯定是对的

B选项我们通过实例证明一下。

具体做法思路为:定义一个私有常量。在一个方法上将常量做自增操作并加锁,然后方法中通过线程名称来判断是否调用sleep方法,开三个线程进行执行,如果第一个线程执行则睡眠 ,此时线程2和线程3也启动。

结果分析:如果第一个线程睡眠后,另外两个线程也进入到常量自增方法,说明sleep是释放锁得,反之不释放。

public class SleepAndWait implements Runnable{
    private int count = 0;
    public synchronized int getCount(String threadName) throws InterruptedException {
        if("Thread-0".equals(threadName)){
            Thread.sleep(15000);
        }

        return count++;
    }
    @Override
    public void run() {
        try {
            String name = Thread.currentThread().getName();
            System.out.println(name+"开始调用...");
            int count1 = getCount(name);
            System.out.println("当前线程:"+name+"获取数据为:"+count1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

   测试:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        SleepAndWait sw = new SleepAndWait();
        Thread t1 = new Thread(sw);
        Thread t2 = new Thread(sw);
        Thread t3 = new Thread(sw);
        t1.start();
        //Thread.sleep(2000);
        t2.start();
        t3.start();
    }
}

结果:


Thread-1开始调用...
当前线程:Thread-1获取数据为:0
Thread-0开始调用...
Thread-2开始调用...
当前线程:Thread-0获取数据为:1
当前线程:Thread-2获取数据为:2

Process finished with exit code 0

因为线程的竞争性,第二个线程先拿到了CPU资源并进行调用,而第一个线程在第二位执行时进入了睡眠状态,此时第三个线程也执行到了该方法,但是因为sleep并不释放锁,所以第三个线程知道第二个线程执行完毕后才执行。

结论:sleep是不释放锁的。

现在测试wait,因为是Object的方法,所以通过针对对象的锁的获取和释放进行测试

代码:

public class WaitDemo implements Runnable{
    Demo demo = new Demo();
    @Override
    public void run() {
        try {
            demo.waitAndNotify(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Demo{
    public synchronized void waitAndNotify(String threadName) throws InterruptedException {
        if("Thread-0".equals(threadName)){
            System.out.println("指定线程"+threadName+"进入因此wait下");
            this.wait();
        }else{
            System.out.println("非指定线程"+threadName+"获取到对象锁执行了任务");
        }
    }
}

测试:

public class TestWait {
    public static void main(String[] args) throws InterruptedException {
        WaitDemo sw = new WaitDemo();
        Thread t1 = new Thread(sw);
        Thread t2 = new Thread(sw);
        Thread t3 = new Thread(sw);
        t1.start();
        //Thread.sleep(2000);
        t2.start();
        t3.start();
    }
}

结果:

指定线程Thread-0进入因此wait下
非指定线程Thread-2获取到对象锁执行了任务
非指定线程Thread-1获取到对象锁执行了任务

第一个线程永远的陷入了睡眠,从执行一直不结束可以看出来。

在第一个线程先执行的情况下,也就是获取了对象锁,在其睡眠wait后,其他线程也进行了对该对象的调用,因此可以说wait是放弃了对象锁。

所以B也是对的

扩展下:

所有对象都自动含有单一的锁。
JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。
只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。
每当任务离开一个synchronized(同步)方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

上面这个是并发编程实战中的一段话,讲述的是锁的重进入

关于锁池和等待池

在Java中,每个对象都有两个池,锁(monitor)池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

深入理解
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

注:wait() ,notifyAll(),notify() 三个方法都是Object类中的方法.

关于wait() ,notifyAll(),notify() 三个方法

  • wait()
    public final void wait() throws InterruptedException,IllegalMonitorStateException

该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用 wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用 wait()方法。进入 wait()方法后,当前线程释放锁。在从 wait()返回前,线程与其他线程竞争重新获得锁。如果调用 wait()时,没有持有适当的锁,则抛出 IllegalMonitorStateException,它是 RuntimeException 的一个子类,因此,不需要 try-catch 结构。

  • notify()
    public final native void notify() throws IllegalMonitorStateException

该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,的如果调用 notify()时没有持有适当的锁,也会抛出 IllegalMonitorStateException。

该方法用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个 wait()状态的线程来发出通知,并使它等待获取该对象的对象锁(notify 后,当前线程不会马上释放该对象锁,wait 所在的线程并不能马上获取该对象锁,要等到程序退出 synchronized 代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象notify的线程们。当第一个获得了该对象锁的 wait 线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用 notify 语句,则即便该对象已经空闲,其他 wait 状态等待的线程由于没有得到该对象的通知,会继续阻塞在 wait 状态,直到这个对象发出一个 notify 或 notifyAll。这里需要注意:它们等待的是被 notify 或 notifyAll,而不是锁。这与下面的 notifyAll()方法执行后的情况不同。

  • notifyAll()
    public final native void notifyAll() throws IllegalMonitorStateException

该方法与 notify ()方法的工作方式相同,重要的一点差异是:

notifyAll 使所有原来在该对象上 wait 的线程统统退出 wait 的状态(即全部被唤醒,不再等待 notify 或 notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll 线程退出调用了 notifyAll 的 synchronized 代码块的时候),他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

C选项分析

监控状态是否存在我们只需要在其wait后进行状态查询就可以了

public class TestWait {
    public static void main(String[] args) throws InterruptedException {
        WaitDemo sw = new WaitDemo();
        Thread t1 = new Thread(sw);
        Thread t2 = new Thread(sw);
        Thread t3 = new Thread(sw);
        t1.start();
        Thread.sleep(2000);
        t2.start();
        t3.start();
        //监控等待线程状态
        System.out.println(t1.getState());
    }
}

结果里面看到:WAITING,所以C也是对的。如果要验证结束后回复,可以在另外线程中将其唤醒并打印。

D选项分析

d选项我认为想成在重新获得对象锁之后首先应该进入到就绪状态,然后再去竞争这个锁。

总结:

1.sleep可以在任何地方使用,wait必须在同步方法或者同步代码块中。

2.线程调用,wait主要针对的是对象锁,即使用该对象需要获得该对象锁。而sleep方法知识暂停当前线程的执行,只不过如果该线程正占用对象的锁时,是不会释放的。

3.个人感觉wait的使用更复杂些。

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值