zoukankan      html  css  js  c++  java
  • JAVA多线程线程阻塞与唤醒

    方式1:早期JAVA采用suspend()、resume()对线程进行阻塞与唤醒,但这种方式产生死锁的风险很大,因为线程被挂起以后不会释放锁,可能与其他线程、主线程产生死锁,如:

    public class ThreadSuspendTest {
        public static void main(String[] args) {
            Thread mt = new MyThread();
            mt.start();
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mt.suspend();
            System.out.println("suspend complete?");
            mt.resume();
        }
        static class MyThread extends Thread {
            public void run() {
                while (true) {
                    System.out.println("running....");
                }
            }
        }
    }
    View Code

    方式2:wait、notify形式通过一个object作为信号,object的wait()方法是锁门的动作,notify()、notifyAll()是开门的动作,某一线程一旦关上门后其他线程都将阻塞,直到别的线程打开门。notify()准许阻塞的一个线程通过,notifyAll()允许所有线程通过。如下例子:主线程分别启动两个线程,随后通知子线程暂停等待,再逐个唤醒后线程抛异常退出。

    public class ObjectWaitTest {
        public static Object waitObject = new Object();
        
        public static void notifyAllThread() {
            System.out.println("notifyAllThread");
            synchronized (waitObject) {
                waitObject.notifyAll();
            }
        }
        public static void notifyThread() {
            System.out.println("notifyThread");
            synchronized (waitObject) {
                waitObject.notify();
            }
        }
        public static void main(String[] args) {
            MyThread tm1 = new MyThread(waitObject);
            tm1.setName("tm1");
            tm1.start();
            MyThread tm2 = new MyThread(waitObject);
            tm2.setName("tm2");
            tm2.start();
            try {
                Thread.currentThread().sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tm1.suspendThread();
            tm2.suspendThread();
            try {
                Thread.currentThread().sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            notifyThread();
            try {
                Thread.currentThread().sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            notifyThread();    
        }
        
        static class MyThread extends Thread {
            public Object waitObject = null;
            private boolean isStop = false;
    
            public MyThread(Object waitObject) {
                this.waitObject = waitObject;
            }
    
            public void run() {
                while (true) {
                    synchronized (waitObject) {
                        if (isStop) {
                            System.out.println(Thread.currentThread().getId() + " is stop");
                            try {
                                waitObject.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getId() + " is resume");
                            System.out.println(Thread.currentThread().getId() + " will  exit");
                            throw new RuntimeException(Thread.currentThread().getId() +" exit");
                        }
                    }
                }
            }
    
            public void suspendThread() {
                this.isStop = true;
            }
        }
    
    }
    View Code

     wait、notify使用要点:

    1、对象操作都需要加同步synchronized;

    2、线程需要阻塞的地方调用对象的wait方法;

    存在的不足:面向对象的阻塞是阻塞当前线程,而唤醒的是随机的一个线程或者所有线程,偏重线程间的通信;同时某一线程在被另一线程notify之前必须要保证此线程已经执行到wait等待点,错过notify则可能永远都在等待。

    方式3:LockSupport提供的park和unpark方法,提供避免死锁和竞态条件,很好地代替suspend和resume组合。

    public class ThreadParkTest {
        public static void main(String[] args) {
            MyThread mt = new MyThread();
            mt.setName("mt");
            mt.start();
            try {
                Thread.currentThread().sleep(10);
                mt.park();
                Thread.currentThread().sleep(30000);
                mt.unPark();
                Thread.currentThread().sleep(30000);
                mt.park();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        static class MyThread extends Thread {
    
            private boolean isPark = false;
            public void run() {
                System.out.println(" Enter Thread running.....");
                while (true) {
                    if (isPark) {
                        System.out.println("Thread is Park.....");
                        LockSupport.park();
                    }
                }
            }
            public void park() {
                isPark = true;
            }
            public void unPark() {
                isPark = false;
                LockSupport.unpark(this);
                System.out.println("Thread is unpark.....");
            }
        }
    }
    View Code

    park与unpark方法控制的颗粒度更加细小,能准确决定线程在某个点停止,进而避免死锁的产生。

    park与unpark引入了许可机制,许可逻辑为:

    ①park将许可在等于0的时候阻塞,等于1的时候返回并将许可减为0;

    ②unpark尝试唤醒线程,许可加1。根据这两个逻辑,对于同一条线程,park与unpark先后操作的顺序似乎并不影响程序正确地执行,假如先执行unpark操作,许可则为1,之后再执行park操作,此时因为许可等于1直接返回往下执行,并不执行阻塞操作。

    park与unpark组合真正解耦了线程之间的同步,不再需要另外的对象变量存储状态,并且也不需要考虑同步锁,wait与notify要保证必须有锁才能执行,而且执行notify操作释放锁后还要将当前线程扔进该对象锁的等待队列,LockSupport则完全不用考虑对象、锁、等待队列等问题。

    总结:suspend()、resume()已经被deprecated,不建议使用。wait、notify需要对对象加同步,性能有折扣。LockSupport则完全不用考虑对象、锁、等待队列。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 阿尔法乘积
    Java实现 蓝桥杯VIP 算法训练 黑色星期五
    Java实现 蓝桥杯VIP 算法训练 黑色星期五
    Java实现 蓝桥杯VIP 算法训练 阶乘
    Java实现 蓝桥杯VIP 算法训练 反置数
    Java实现 蓝桥杯VIP 算法训练 新生舞会
    Android消息机制架构和源码解析
    华为接班人制度(效率优先,兼顾公平,可持续发展)
    VMware与宿敌Amazon一笑泯恩仇:重新定义混合云?(私有云节节败退)
    UAC新解(有非正常手段可以绕过)
  • 原文地址:https://www.cnblogs.com/heyq/p/9013783.html
Copyright © 2011-2022 走看看