Java并发--sleep()、wait()、notify()、notifyAll()方法

sleep()和wait方法比较 

基本差别:

1,sleep是Thread类中的native方法、wait是Object类中的方法。

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds, subject to
 * the precision and accuracy of system timers and schedulers. The thread
 * does not lose ownership of any monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public static native void sleep(long millis) throws InterruptedException;
/**
 * Causes the current thread to wait until it is awakened, typically
 * by being <em>notified</em> or <em>interrupted</em>.
 * <p>
 * In all respects, this method behaves as if {@code wait(0L, 0)}
 * had been called. See the specification of the {@link #wait(long, int)} method
 * for details.
 *
 * @throws IllegalMonitorStateException if the current thread is not
 *         the owner of the object's monitor
 * @throws InterruptedException if any thread interrupted the current thread before or
 *         while the current thread was waiting. The <em>interrupted status</em> of the
 *         current thread is cleared when this exception is thrown.
 * @see    #notify()
 * @see    #notifyAll()
 * @see    #wait(long)
 * @see    #wait(long, int)
 */
public final void wait() throws InterruptedException {
    wait(0L);
}

2,sleep方法可以在任何地方使用,wait方法只能在synchronized方法或者synchronized块中使用。

 

主要区别:

1,Thread.sleep 只会让出cpu,不会导致锁行为改变。

2,Object.wait 不仅会让出cpu,还会释放占用的同步资源锁。其他线程可以得到锁。

wait传

 

demo:

public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread A get lock");
                        Thread.sleep(20);
                        System.out.println("thread A do wait method");
                        lock.wait(1000); //等待1000ms,让出cpu、锁
                        System.out.println("thread A is done");
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        try{
            Thread.sleep(10);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread B get lock");
                        System.out.println("thread B is sleeping 10 ms");
                        Thread.sleep(10);
                        lock.notifyAll(); // 唤醒其他所有线程,将A放入锁池当中去竞争锁,但是不释放锁,继续执行代码
                        Thread.yield(); //yeild 不会对锁造成影响。总是B先执行完。
                        Thread.sleep(2000); // 对锁没影响
                        System.out.println("thread B is done");
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }
}

执行结果:

thread A is waiting to get lock
thread A get lock
thread B is waiting to get lock //A 的sleep方法不让出锁。B等待
thread A do wait method //A wait方法 让出锁,A
thread B get lock //A让出锁 B获得锁
thread B is sleeping 10 ms 
thread B is done //B执行完毕
thread A is done //A的wait(1000),到期自动唤醒,正好B释放锁。A可以得到锁

 可以看到,A虽然执行sleep方法,让出CPU,但不让出锁。如果不执行wait(1000)方法,B会一直等待获取锁。

wait方法,必须写在synchronized,因为必须要先获取锁,才能去释放锁。

 

 notify(),notifyAlll()方法:

上面demo中,通过wait(1000),设置参数1000ms后,A线程会自动唤醒。

也可以直接用wait(),不设置自动唤醒,可以在B线程中使用Object.notify()唤醒A线程。

demo:

public class NotificationDemo {
    private volatile boolean go = false;

    public static void main(String args[]) throws InterruptedException {
        final NotificationDemo test = new NotificationDemo();

        Runnable waitTask = new Runnable(){

            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " finished Execution");
            }
        };

        Runnable notifyTask = new Runnable(){

            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread().getName() + " finished Execution");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1"); //will wait
        Thread t2 = new Thread(waitTask, "WT2"); //will wait
        Thread t3 = new Thread(waitTask, "WT3"); //will wait
        Thread t4 = new Thread(notifyTask,"NT1"); //will notify

        //starting all waiting thread
        t1.start();
        t2.start();
        t3.start();

        //pause to ensure all waiting thread started successfully
        Thread.sleep(200);

        //starting notifying thread
        t4.start();

    }
    /*
     * wait and notify can only be called from synchronized method or bock
     */
    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + " is going to wait on this object");
            wait(); //release lock and reacquires on wakeup
            System.out.println(Thread.currentThread() + " is woken up");
        }
        go = false; //resetting condition
    }

    /*
     * both shouldGo() and go() are locked on current object referenced by "this" keyword
     */
    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " is going to notify all or one thread waiting on this object");

            go = true; //making condition true for waiting thread
            //notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up
            notifyAll(); // all waiting thread  WT1, WT2,WT3 will woke up
        }

    }
}

结果:

Thread[WT1,5,main] is going to wait on this object
Thread[WT3,5,main] is going to wait on this object
Thread[WT2,5,main] is going to wait on this object
Thread[NT1,5,main] is going to notify all or one thread waiting on this object
Thread[WT1,5,main] is woken up
Thread[WT2,5,main] is woken up
Thread[WT2,5,main] is going to wait on this object
NT1 finished Execution
WT1 finished Execution
Thread[WT3,5,main] is woken up
Thread[WT3,5,main] is going to wait on this object

 

两个概念(针对对象):

锁池entryList。会去竞争锁。若B线程被阻塞,进入该对象的锁池,等待锁的释放,去竞争锁。如果多个不同优先级的线程竞争锁,优先级高获取锁的概率大。

等待池WaitSet。不会去竞争锁。A调用wait()方法,会释放该对象的锁,同时进入等待池中,不竞争锁。

 

 

yield)()方法:

当调用该函数时,调度器会给线程调度器一个 当前线程愿意出让CPU使用的暗时,但是线程调度器可能会忽略。

     A hint to the scheduler that the current thread is willing to yield
     its current use of a processor. The scheduler is free to ignore this
     hint.

会让出当前现线程CPU,但不会改变锁的状态。

 

如何中断线程

直接使用stop()可能会导致异常,不推荐。

调用interrupt(),通知线程应该中断了。

如果是:阻塞线程,会退出并抛出InterruptException异常;正常运行线程,会将中断标志位 设置为true,继续运行。

 

demo:

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable interruptTask = new Runnable() {
            @Override
            public void run() {
                int i = 0;
                try {
                    //在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
                    while (!Thread.currentThread().isInterrupted()) {
                        Thread.sleep(100); // 休眠100ms
                        i++;
                        System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") loop " + i);
                    }
                } catch (InterruptedException e) {
                    //在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
                    System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") catch InterruptedException.");
                }
            }
        };
        Thread t1 = new Thread(interruptTask, "t1");
        System.out.println(t1.getName() +" ("+t1.getState()+") is new.");

        t1.start();                      // 启动“线程t1”
        System.out.println(t1.getName() +" ("+t1.getState()+") is started.");

        // 主线程休眠300ms,然后主线程给t1发“中断”指令。
        Thread.sleep(300);
        t1.interrupt();
        System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");

        // 主线程休眠300ms,然后查看t1的状态。
        Thread.sleep(300);
        System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
    }
}

运行结果:

t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (RUNNABLE) catch InterruptedException. //线程调用sleep方法处于阻塞状态,调用interrupt方法,catch到异常后抛出异常。
t1 (TIMED_WAITING) is interrupted. //t1会不断检查中断标志位
t1 (TERMINATED) is interrupted now. // 已经被中断

 

总结:interrupt() 并不能主动中断线程,运行时会主动检查标志位,如果是true,自行停止线程。

 

posted on 2023-05-25 23:39  passionConstant  阅读(51)  评论(0编辑  收藏  举报