• Thread 的 wait、sleep、join、yied 理解


    一.查看API

    sleep是Thread类的方法,导致此线程暂停执行指定时间,给其他线程执行机会,但是依然保持着监控状态,过了指定时间会自动恢复,调用sleep方法不会释放锁对象。

    当调用sleep方法后,当前线程进入阻塞状态。目的是让出CPU给其他线程运行的机会。但是由于sleep方法不会释放锁对象,所以在一个同步代码块中调用这个方法后,线程虽然休眠了,但其他线程无法访问它的锁对象。这是因为sleep方法拥有CPU的执行权,它可以自动醒来无需唤醒。而当sleep()结束指定休眠时间后,这个线程不一定立即执行,因为此时其他线程可能正在运行。

    wait方法是Object类里的方法,当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放了锁对象,等待期间可以调用里面的同步方法,其他线程可以访问,等待时不拥有CPU的执行权,否则其他线程无法获取执行权。当一个线程执行了wait方法后,必须调用notify或者notifyAll方法才能唤醒,而且是随机唤醒,若是被其他线程抢到了CPU执行权,该线程会继续进入等待状态。由于锁对象可以时任意对象,所以wait方法必须定义在Object类中,因为Obeject类是所有类的基类。

    二.是否可以传入参数

    sleep()方法必须传入参数,参数就是休眠时间,时间到了就会自动醒来。

    wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不穿如参数就是直接等待。

    三.是否需要捕获异常

    sleep方法必须要捕获异常,而wait方法不需要捕获异常。sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。

    wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait()方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生。

    四.作用范围

    wait、notify和notifyAll方法只能在同步方法或者同步代码块中使用,而sleep方法可以在任何地方使用。但是注意sleep是静态方法,也就是说它只对当前对象有效。通过对象名.sleep()想让该对象线程进入休眠是无效的,它只会让当前线程进入休眠。

    五.调用者的区别

    首先为什么wait、notify和notifyAll方法要和synchronized关键字一起使用?

    因为wait方法是使一个线程进入等待状态,并且释放其所持有的锁对象,notify方法是通知等待该锁对象的线程重新获得锁对象,然而如果没有获得锁对象,wait方法和notify方法都是没有意义的,因此必须先获得锁对象再对锁对象进行进一步操作于是才要把wait方法和notify方法写到同步方法和同步代码块中了。

    由此可知,wait和notify、notifyAll方法是由确定的对象即锁对象来调用的,锁对象就像一个传话的人,他对某个线程说停下来等待,然后对另一个线程说你可以执行了(实质上是被捕获了),这一过程是线程通信。sleep方法是让某个线程暂停运行一段时间,其控制范围是由当前线程决定,运行的主动权是由当前线程来控制(拥有CPU的执行权)。

    其实两者的区别都是让线程暂停运行一段时间,但本质的区别一个是线程的运行状态控制,一个是线程间的通信。

    函数名 作用
    wait    

    当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放了对象的锁,使得其他线程可以访问。用户可以使用notify、notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。

    注意:wait()、notify()、notifyAll()必须放在synchronized block中,否则会抛出异常

    wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不穿如参数就是直接等待。

    wait方法不需要捕获异常

    sleep   

    该函数是Thread的静态函数,作用是使调用线程进入睡眠状态。因为sleep()是Thread类的Static方法,因此他不能改变对象的锁。所以,当在一个Sychronized块中调用sleep()方法,线程虽然休眠了,但是对象的锁并没有释放,其他线程无法访问这个对象(即使睡着也持有对象锁)

    sleep()方法必须传入参数,参数就是休眠时间,时间到了就会自动醒来。

    sleep方法必须要捕获异常

    join 等待目标线程执行完成之后再继续执行
    yield 线程礼让。目标线程由运行状态转换为就绪状态,也就是让出执行权限,让其他线程得以优先执行,但其他线程能否优先执行是未知数

    package com.adao.thread.yeild;
    
    public class YieldThread extends Thread {
        public YieldThread(String name) {
            super(name);
        }
    
        public synchronized void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(String.format("%s [%d] ---> %d", this.getName(), this.getPriority(), i));
                // 当i为2时调用当前线程的yield函数
    //            if (i == 2) {
    //                Thread.yield();
    //            }
            }
        }
    
        public static void main(String[] args) {
            YieldThread t1 = new YieldThread("thread-1");
            YieldThread t2 = new YieldThread("thread-2");
            t1.start();
            t2.start();
        }
    }

    结果:

     取消屏蔽:

     我们看到执行到的thread-2  等于2之前交替执行,等于2时,thread-2让出执行时间,执行thread-1,thread-1执行到2时又将执行权让出

     JOIN

    package com.adao.thread.yeild;
    
    public class Worker extends Thread {
        public Worker(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("work in " + getName());
        }
    
        public static void main(String[] args) {
            Worker worker1 = new Worker("work-1");
            Worker worker2 = new Worker("work-2");
            worker1.start();
            System.out.println("启动线程1");
            try {
                // 调用work1的join函数,主线程会阻塞直到work1执行完成
                worker1.join();
                System.out.println("启动线程2");
                // 再启动线程2,并且调用线程2的join函数,主线程会阻塞直到worker2执行完成
                worker2.start();
                worker2.join();
            } catch (Exception ignored) {
    
            }
            System.out.println("主线程继续执行");
        }
    }

    Wait:

    package com.adao.thread.yeild;
    
    public class Wait {
        private static Object sLockObject = new Object();
    
        static void waitAndNotifyAll() {
            System.out.println("主线程运行");
            Thread thread = new WaitThread();
            thread.start();
            long startTime = System.currentTimeMillis();
            try {
                synchronized (sLockObject) {
                    System.out.println("主线程等待");
                    sLockObject.wait();
                }
            } catch (Exception ignored) {
    
            }
            long timsMs = (System.currentTimeMillis() - startTime);
            System.out.println("主线程继续-->等待耗时:" + timsMs + "ms");
        }
    
        static class WaitThread extends Thread {
            @Override
            public void run() {
                try {
                    synchronized (sLockObject) {
                        Thread.sleep(3000);
                        sLockObject.notifyAll();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            Wait.waitAndNotifyAll();
        }
    }

  • 相关阅读:
    eclipse中打断点debug无效
    Jenkins构建部署Maven项目
    自动生成实体类和xml
    session 控制单点登录
    eclipse team 没有svn
    easyui combobox 设置滚动条
    unkow jdbc driver : http://maven.apache.org
    easyui datagrid列显示图片
    mysql中计算日期整数差
    Map<String, Object>转Object,Object转 Map<String, Object>
  • 原文地址:https://www.cnblogs.com/adao21/p/13298053.html
Copyright © 2020-2023  润新知