博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA多线程--线程阻塞与唤醒
阅读量:5155 次
发布时间:2019-06-13

本文共 4992 字,大约阅读时间需要 16 分钟。

方式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则完全不用考虑对象、锁、等待队列。

 

转载于:https://www.cnblogs.com/heyq/p/9013783.html

你可能感兴趣的文章
Notes of Daily Scrum Meeting(12.8)
查看>>
Apriori算法
查看>>
onlevelwasloaded的调用时机
查看>>
求出斐波那契数组
查看>>
Vue.js 基础学习之组件通信
查看>>
lr_start_transaction/lr_end_transaction事物组合
查看>>
每天一个Linux命令 - 【chkconfig】
查看>>
△UVA10106 - Product(大数乘法)
查看>>
golang (7) 文件操作
查看>>
关于 Object.defineProperty()
查看>>
[转] Maven 从命令行获取项目的版本号
查看>>
CodeIgniter学习笔记(四)——CI超级对象中的load装载器
查看>>
.NET CLR基本术语
查看>>
ubuntu的home目录下,Desktop等目录消失不见
查看>>
建立,查询二叉树 hdu 5444
查看>>
[Spring框架]Spring 事务管理基础入门总结.
查看>>
2017.3.24上午
查看>>
Python-常用模块及简单的案列
查看>>
(VC/MFC)多线程(Multi-Threading) -1. 基本概念.
查看>>
快数据时代下,Moka携手DataPipeline提升招聘效能
查看>>