Java线程通信

线程通信

线程通信指的是多个线程在运行的期间,相互之间的数据交互协作。

1.通信方式

实现多个线程直接的协作,涉及到的通信方式主要四类。
1)文件共享
2)网络共享
3)共享变量
4)JDK提供的线程协调API

1.文件共享
 线程A写文件,线程B读取文件达到线程协作。
2.网络共享
 线程A发送数据,线程B接受数据达到线程协作。
3.共享变量
 利用内存的公共区域,共享变量。线程A修改变量,线程B读取变量达到线程协作。

以上3种方式都是比较触及的,我们主要链接JDK给我提供的API

2.线程协作 JDK API

生产者消费者模型是线程协作的典型场景(线程阻塞、线程唤醒),我们以一个示例去理解
示例:线程A提车,没有车,则不执行。线程B有车了,通知线程A继续执行

2.1 suspend/resume

suspend挂起线程,resume恢复线程执行。这两个API是Thread提供的。
示例:

public static Object benz = null;
public void suspendResumeTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            if (benz == null) { // 如果没奔驰,则进入等待
                System.out.println("1、进入等待");
                Thread.currentThread().suspend();
            }
            System.out.println("2、提到车,回家");
        });
        consumerThread.start();
        // 3秒之后,拉来一辆奔驰
        Thread.sleep(3000L);
        benz = new Object();
        consumerThread.resume();
        System.out.println("3、通知消费者");
    }

suspend/resume虽然是Thread提供的,因为很容易写出死锁的代码。 所以被弃用了。
死锁示例1:在写同步代码的时候容易出现:suspend在挂起之后并不会释放锁

public void suspendResumeDeadLockTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            if (benz == null) { // 如果没奔驰,则进入等待
                System.out.println("1、进入等待");
                // 当前线程拿到锁,然后挂起
                synchronized (this) {
                    Thread.currentThread().suspend();
                }
            }
            System.out.println("2、提到车,回家");
        });
        consumerThread.start();
        // 3秒之后,拉来一辆奔驰
        Thread.sleep(3000L);
        benz = new Object();
        // 争取到锁以后,再恢复consumerThread
        synchronized (this) {
            consumerThread.resume();
        }
        System.out.println("3、通知消费者");
    }

这种情况下消费者如果拿到锁,消费者就挂起了,生产者要通知消费者必须要抢到锁,但是因为消费者抢到锁之后挂起,并没有释放锁,生产者是抢不到锁,这就死锁了。
死锁示例2: API调用顺序:suspend在resume后执行死锁

public void suspendResumeDeadLockTest2() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            if (benz == null) {
                System.out.println("1、没奔驰,进入等待");
                try { // 为这个线程加上一点延时
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 这里的挂起执行在resume后面
                Thread.currentThread().suspend();
            }
            System.out.println("2、提到车,回家");
        });
        consumerThread.start();
        // 3秒之后,拉来一辆奔驰
        Thread.sleep(3000L);
        benz = new Object();
        consumerThread.resume();
        System.out.println("3、通知消费者");
        consumerThread.join();
    }

这种情况下suspend在resume后执行,线程恢复不了执行,死锁了。

2.2 wait/notify

这一对API只能由统一对象锁的持有者调用,也就是写在同步代码块里面,否则抛异常。
wait使当前线程等待,加入该对象的等待池,并释放锁。
notify/notifyAll唤醒一个或所有正在等待这个对象的线程
因为必须用在同步代码块里面, wait/notify针对锁的问题不存在了。但是顺序需要注意。
示例:

public void waitNotifyTest() throws Exception {
       // 启动线程
       new Thread(() -> {
           if (benz == null) { // 如果没奔驰,则进入等待
               synchronized (this) {
                   try {
                       System.out.println("1、进入等待");
                       this.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
           System.out.println("2、提到车,回家");
       }).start();
       // 3秒之后,拉来一辆奔驰
       Thread.sleep(3000L);
       benz = new Object();
       synchronized (this) {
           this.notifyAll();
           System.out.println("3、通知消费者");
       }
   }

注意:虽然wait会释放锁,但是对顺序有要求,如果wait在notify之调用,线程就永远处于WAITING状态了。
死锁示例:

public void waitNotifyDeadLockTest() throws Exception {
       // 启动线程
       new Thread(() -> {
           if (benz == null) { // 如果没奔驰,则进入等待
               try {
                   Thread.sleep(5000L);
               } catch (InterruptedException e1) {
                   e1.printStackTrace();
               }
               synchronized (this) {
                   try {
                       System.out.println("1、进入等待");
                       this.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
           System.out.println("2、提到车,回家");
       }).start();
       // 3秒之后,拉来一辆奔驰
       Thread.sleep(3000L);
       benz = new Object();
       synchronized (this) {
           this.notifyAll();
           System.out.println("3、通知消费者");
       }
   }

这种情况下wait在notify后执行,线程收不到通知,死锁了。

2.3 park/unpark

park等待许可,unpark提供许可令牌,让线程继续执行,park/unpark没有顺序要求。
多次unpark之后调用park,线程会直接执行,因为已经拿到许可令牌了。
许可令牌不会叠加,多次调用park,只有第一次会拿到许可,继续执行,后续的调用则进入等待。
示例:

public void parkUnparkTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            if (benz == null) { // 如果没奔驰,则进入等待
                System.out.println("1、进入等待");
                LockSupport.park();
            }
            System.out.println("2、提到车,回家");
        });
        consumerThread.start();
        // 3秒之后,拉来一辆奔驰
        Thread.sleep(3000L);
        benz = new Object();
        LockSupport.unpark(consumerThread);
        System.out.println("3、通知消费者");
    }

park/unpark不会释放锁,所以在同步代码块里面使用不当就容易死锁。
死锁示例:

public void parkUnparkDeadLockTest() throws Exception {
        // 启动线程
        Thread consumerThread = new Thread(() -> {
            if (benz == null) { // 如果没奔驰,则进入等待
                System.out.println("1、进入等待");
                // 当前线程拿到锁,然后挂起
                synchronized (this) {
                    LockSupport.park();
                }
            }
            System.out.println("2、提到车,回家");
        });
        consumerThread.start();
        // 3秒之后,拉来一辆奔驰
        Thread.sleep(3000L);
        benz = new Object();
        // 争取到锁以后,再恢复consumerThread
        synchronized (this) {
            LockSupport.unpark(consumerThread);
        }
        System.out.println("3、通知消费者");
    }

消费者挂起之后没有释放锁,生产者永远获取不到锁,死锁。

2.4 join

有人说join也是一种,其实join底层使用的wait/notify

结语

虽然都是些简单例子,但是我们通过这些例子去看正确的操作,还有死锁的情况,我可以在平时写这类代码的时候可以避免踩坑。弃用的suspend/resume就不要用了,wait/notify、park/unpark看场景需要使用。
第一次写博客,排版乱糟糟,有很多知识点还没完全讲到,讲得不够详细,多多谅解。迈出一步,那也是进步。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  •   谈到并发我们就会想到多线程,要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行的结果等等。都涉...
    TodoCoder阅读 522评论 0 4
  • 转自线程通信 线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。 例如,线...
    骑摩托马斯阅读 479评论 0 3
  • 利用共享对象实现通信 忙等(busy waiting) wait(), notify() and notifyAl...
    六尺帐篷阅读 4,799评论 3 23
  • 传统的线程通信 假设现在系统当中有两个线程,这两个进程分别代表了存款者和取钱者。现在假设系统有一个特殊的需求,系统...
    BlueSkyBlue阅读 247评论 0 0
  • “怎么还慢吞吞的!”,JVM翘着二郎腿在调度室里大声喊道。“Thread-2,你已经被我创建出来了,赶紧干活!”。...
    tery007阅读 549评论 0 3