线程间的通讯

  

  直接上一个Demo,模拟生产者和消费者,就是生产了一个物品,然后消费一个物品,就这样你一下,我一下,让他们有类似有通讯的功能一样知道彼此生产了而且消费了。这里需要考虑两个问题面向这个demo时候,第一是线程的安全问题,第二是通讯问题,你一下,我一下

 1 package com.thread;
 2 // 定义一个共享的数据
 3 class User {
 4     public String name;
 5     public String age;
 6     boolean flag = true;  // 稍后会用到
 7 }
 8
 9 // 模拟生产者
10 class Input extends Thread {
11     // 把共享变量传递当生产者中
12     public User u;
13     Input(User u) {
14         this.u = u;
15     }
16     int count = 1;// count只是为了逻辑控制
17
18     @Override
19     public void run() {
20
21         while (true) {
22             if (count==0) {
23                 u.name = "剑二十三";
24                 u.age = "999";
25             } else {
26                 u.name = "XXX";
27                 u.age = "123";
28             }
29             count = (count+1)%2;// 逻辑控制count等于0 和不等于0
30
31         }
32
33     }
34 }
35
36 class Out extends Thread {
37 // 共享变量传入消费者中
38     public User u;
39     Out(User u) {
40         this.u = u;
41     }
42
43     @Override
44     public void run() {
45         while (true) {
46          47                 System.out.println(u.name + "----" + u.age);
48 49
50         }
51     }
52 }
53
54 // 模拟生产者和消费者,线程间的通讯
55 public class ThreadDemo4 {
56     public static void main(String[] args) {
57         // 创建共享变量,并创建两个线程,把共享变量出入其中。开启两个线程。
58         // 一般理想状态是为了打印         剑二十三+999     与     XXX+123   两种字符串  并且 每次你输出一下我输出一下。
59         // 实际代码打印的确是    剑二十三+123  或者     XXX+999  并且还不是 一行一个样,你输出一下我输出一下。
60         User u = new User();
61         Input i = new Input(u);
62         Out o = new Out(u);
63         i.start();
64         o.start();
65 //         部分打印结果
66 //        剑二十三----123
67 //        剑二十三----999
68 //        XXX----999
69 //        XXX----999
70 //        XXX----123
71
72     }
73
74 }

产生这种结果的原因:

  1.因为线程之间出现了并发情况,在赋值的过程中,可能刚赋值完  ‘剑二十三’  还有没赋值  ‘999’ 的信息,就打印了,所以打印的是上次保存共享变量的结果。

  2.线程之间没有进行通讯,通过打印答案可以知道,并没有你生产一个我消费一个,你才能在生产一个,我在消费一个。

解决:

  首先解决线程安全问题,我们首先想到synchronized 那么我们就用 synchronized 先来实现线程的安全问题,在解决通讯问题。

 1 package com.thread;
 2
 3 // 定义一个共享的数据
 4 class User {
 5     public String name;
 6     public String age;
 7     boolean flag = true;  // 稍后通讯时候可能会用到
 8 }
 9
10 // 模拟生产者
11 class Input extends Thread {
12     // 把共享变量传递当生产者中
13     public User u;
14
15     Input(User u) {
16         this.u = u;
17     }
18
19     int count = 1;// count只是为了逻辑控制
20
21     @Override
22     public void run() {
23
24         while (true) {
25             synchronized (u) {    // 因为要用同一把锁,选定为对象锁 u
26                 if (count == 0) {
27                     u.name = "剑二十三";
28                     u.age = "999";
29                 } else {
30                     u.name = "XXX";
31                     u.age = "123";
32                 }
33                 count = (count + 1) % 2;// 逻辑控制count等于0 和不等于0
34
35             }
36
37         }
38
39     }
40 }
41
42 class Out extends Thread {
43     // 共享变量传入消费者中
44     public User u;
45
46     Out(User u) {
47         this.u = u;
48     }
49
50     @Override
51     public void run() {
52         while (true) {
53             synchronized (u) {    // 因为要用同一把锁,选定为对象锁 u
54               55                     System.out.println(u.name + "----" + u.age);
56 57
58             }
59         }
60     }
61 }
62
63 // 模拟生产者和消费者,线程间的通讯
64 public class ThreadDemo4 {
65     public static void main(String[] args) {
66         // 创建共享变量,并创建两个线程,把共享变量出入其中。开启两个线程。
67         // 实现了线程之间的安全问题,但是并没有实现通讯
68         User u = new User();
69         Input i = new Input(u);
70         Out o = new Out(u);
71         i.start();
72         o.start();
73         // 部分打印结果
74 //        剑二十三----999
75 //        XXX----123
76 //        剑二十三----999
77 //        XXX----123
78 //        XXX----123
79 //        剑二十三----999
80     }
81
82 }

到了这步,已经解决了线程的安全问题。但是通讯问题没有得到解决。解决通讯问题有几个方法 wait(), notify() ,notifyAll()

wait()、notify、notifyAll()方法

wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。

这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。

1.如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。

2.如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。

3.如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

注意:一定要在线程同步中使用,并且是同一个锁的资源

  1 package com.thread;
  2
  3 // 定义一个共享的数据
  4 class User {
  5     public String name;
  6     public String age;
  7     boolean flag = true;
  8 }
  9
 10 // 模拟生产者
 11 class Input extends Thread {
 12     // 把共享变量传递当生产者中
 13     public User u;
 14
 15     Input(User u) {
 16         this.u = u;
 17     }
 18
 19     int count = 1;// count只是为了逻辑控制
 20
 21     @Override
 22     public void run() {
 23
 24         while (true) {
 25             synchronized (u) { // 因为要用同一把锁,选定为对象锁 u
 26
 27                 if (!u.flag) {
 28                     try {
 29                         u.wait();
 30                     } catch (InterruptedException e) {
 31                         e.printStackTrace();
 32                     }
 33                 }
 34
 35                 if (count == 0) {
 36                     u.name = "剑二十三";
 37                     u.age = "999";
 38                 } else {
 39                     u.name = "XXX";
 40                     u.age = "123";
 41                 }
 42                 count = (count + 1) % 2;// 逻辑控制count等于0 和不等于0
 43                 u.flag = false; // 把共享flag变为false
 44                 u.notify();
 45             }
 46
 47         }
 48
 49     }
 50 }
 51
 52 class Out extends Thread {
 53     // 共享变量传入消费者中
 54     public User u;
 55
 56     Out(User u) {
 57         this.u = u;
 58     }
 59
 60     @Override
 61     public void run() {
 62         while (true) {
 63             synchronized (u) { // 因为要用同一把锁,选定为对象锁 u
 64                 if (u.flag) {
 65                     try {
 66                         u.wait();
 67                     } catch (InterruptedException e) {
 68                         e.printStackTrace();
 69                     }
 70
 71                     System.out.println(u.name + "----" + u.age);
 72                 }
 73                 u.flag = true; // 把共享变量flag变为true
 74                 u.notify();
 75             }
 76         }
 77     }
 78 }
 79
 80 // 模拟生产者和消费者,线程间的通讯
 81 public class ThreadDemo4 {
 82     public static void main(String[] args) {
 83         // 创建共享变量,并创建两个线程,把共享变量出入其中。开启两个线程。
 84         // 实现了通讯
 85         User u = new User();
 86         Input i = new Input(u);
 87         Out o = new Out(u);
 88         i.start();
 89         o.start();
 90         // 部分打印结果
 91 //        剑二十三----999
 92 //        XXX----123
 93 //        剑二十三----999
 94 //        XXX----123
 95 //        剑二十三----999
 96 //        XXX----123
 97 //        剑二十三----999
 98 //        XXX----123
 99     }
100
101 }

wait() 把持有该对象的线程控制权交出去!一定要充分理解这句话,只有交出去,第二个线程传过来的 notify() 才能够解锁它,所以他们两个互相解锁唤醒,就实现了通讯的功能。

并且不能单独使用必须在线程同步中使用!!!否则就会报错

wait与sleep区别?

  对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

获取对象锁进入运行状态。

第二种同步锁的功能 Lock锁 

Lock 接口与 synchronized 关键字的区别

Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。

Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。

Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。

Lock写法


Lock lock 
= new ReentrantLock();

lock.lock();

try{

//可能会出现线程安全的操作

}finally{

//一定在finally中释放锁

//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常

lock.unlock();

}

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class User {
    public String name;
    public String age;
    boolean flag = true;
    // 创建锁
    Lock lock = new ReentrantLock();
}
// 模拟生产者
class Input extends Thread {
    // 把共享变量传递当生产者中
    public User u;
    Input(User u) {
        this.u = u;
    }
    int count = 1;// count只是为了逻辑控制

    @Override
    public void run() {

        while (true) {

            u.lock.lock();    // 上锁,逻辑代码用try起来,可能会抛出异常,所以汉子型finally解锁
            if (!u.flag) {

                try {
                    if (count == 0) {
                        u.name = "剑二十三";
                        u.age = "999";
                    } else {
                        u.name = "XXX";
                        u.age = "123";
                    }
                    count = (count + 1) % 2;// 逻辑控制count等于0 和不等于0
                    u.flag = false; // 把共享flag变为false
                    // u.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    u.lock.unlock();
                }
            }
        }

    }
}

class Out extends Thread {
    // 共享变量传入消费者中
    public User u;

    Out(User u) {
        this.u = u;
    }

    @Override
    public void run() {
        while (true) {
            u.lock.lock();// 上锁,逻辑代码用try起来,可能会抛出异常,所以汉子型finally解锁
            try {
                if (u.flag) {
                    System.out.println(u.name + "----" + u.age);
                    u.flag = true; // 把共享变量flag变为true
                }
            } catch (Exception e) {
            } finally {
                u.lock.unlock();
            }
        }
    }
}

// 模拟生产者和消费者,线程间的通讯
public class ThreadDemo4 {
    public static void main(String[] args) {
        User u = new User();
        Input i = new Input(u);
        Out o = new Out(u);
        i.start();
        o.start();
    }

}

但是这次的打印结果又不一样了,虽然实现了高效率的锁,但是通讯没了,打印结果是程序在运行,没用打印记录。这时候需要使用 Condition

用Condition 代替 nodity 和 wait

Condition用法

 Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。

代码


Condition condition = lock.newCondition();

res. condition.await();  类似wait

res. Condition. Signal() 类似notify

  

  1 package com.lock;
  2
  3 import java.util.concurrent.locks.Condition;
  4 import java.util.concurrent.locks.Lock;
  5 import java.util.concurrent.locks.ReentrantLock;
  6
  7 class User {
  8     public String name;
  9     public String age;
 10     boolean flag = true;
 11     // 创建锁
 12     Lock lock = new ReentrantLock();
 13 }
 14
 15 // 模拟生产者
 16 class Input extends Thread {
 17     // 把共享变量传递当生产者中
 18     public User u;
 19     Condition condition;
 20
 21     Input(User u, Condition condition) {
 22         this.condition = condition;
 23         this.u = u;
 24     }
 25
 26     int count = 1;// count只是为了逻辑控制
 27
 28     @Override
 29     public void run() {
 30
 31         while (true) {
 32             try {
 33                 u.lock.lock(); // 上锁,逻辑代码用try起来,可能会抛出异常,所以汉子型finally解锁
 34                 if (!u.flag) {
 35                     condition.await();
 36                 }
 37                 if (count == 0) {
 38                     u.name = "剑二十三";
 39                     u.age = "999";
 40                 } else {
 41                     u.name = "XXX";
 42                     u.age = "123";
 43                 }
 44                 count = (count + 1) % 2;// 逻辑控制count等于0 和不等于0
 45                 u.flag = false; // 把共享flag变为false
 46                 condition.signal();
 47             } catch (Exception e) {
 48             } finally {
 49                 u.lock.unlock();
 50             }
 51
 52         }
 53
 54     }
 55 }
 56
 57 class Out extends Thread {
 58     // 共享变量传入消费者中
 59     public User u;
 60     Condition condition;
 61
 62     Out(User u, Condition condition) {
 63         this.condition = condition;
 64         this.u = u;
 65     }
 66
 67     @Override
 68     public void run() {
 69         while (true) {
 70             try {
 71                 u.lock.lock();// 上锁,逻辑代码用try起来,可能会抛出异常,所以汉子型finally解锁
 72
 73                 if (u.flag) {
 74                     try {
 75                         condition.await();
 76                     } catch (InterruptedException e1) {
 77                         e1.printStackTrace();
 78                     }
 79                 }
 80                 u.flag = true; // 把共享变量flag变为true
 81                 System.out.println(u.name + "----" + u.age);
 82                 condition.signal();
 83
 84             } catch (Exception e) {
 85
 86             } finally {
 87                 u.lock.unlock();
 88             }
 89
 90         }
 91     }
 92 }
 93
 94 // 模拟生产者和消费者,线程间的通讯
 95 public class ThreadDemo4 {
 96     public static void main(String[] args) {
 97         User u = new User();
 98         Condition condition = u.lock.newCondition();
 99         Input i = new Input(u, condition);
100         Out o = new Out(u, condition);
101         i.start();
102         o.start();
103     }
104
105 }

以上这几种就是配合使用的线程通讯问题的解决方式。

原文地址:https://www.cnblogs.com/liclBlog/p/9380476.html

时间: 2024-08-28 02:24:15

线程间的通讯的相关文章

黑马程序员——java——线程间的通讯 生产者与消费者

线程间的的通讯  生产者与消费者 public class TestDemos3 { public static void main(String[] args) { Res r = new Res(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } } class Res

进程和线程的关系及区别,进程间如何通讯,线程间如何通讯

1 定义 进程:进程是程序处理机上的一次执行过程, 它是一个动态的概念,它是系统进行资源分配和调度的一个独立单位. 线程:进程的一个实体,是CPU运行调度的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 2 关系 一个线程可以创建和撤销另一个线程:同一个进程中的多个线程之间可以并发执行: 相对进程而言,线程是一个更加接近于执行体的概念,它可

一个线程间的通讯小程序__(Java_Thread_Inout.Output)

1 //多线程通讯 2 //多个线程处理同一资源,但是任务不同 3 //等待唤醒方法: 4 //wait():将线程变成为冻结状态,线程会被存储在线程池中; 5 //notify():唤醒线程中的一个线程(任意的) 6 //notifyAll():唤醒所有线程; 7 /**************************************************************/ 8 //建立资源类 9 class Resource 10 { 11 private boolean

java多线程——线程间的通讯

public class ThreadDemo3 { public static void main(String[] args) { Resource res = new Resource(); Input input = new Input(res); Output output = new Output(res); Thread t1 = new Thread(input); Thread t2 = new Thread(output); t1.start(); t2.start(); }

利用线程间的通讯实现的水池加水放水

业务需求,加水速度5L/s,放水速度2L/s,加水时不能放水,放水时不能加水,水池容量500L class Pool{ int volume = 0; int maxVolume = 500; int waterInSpeed = 5; int waterOutSpeed = 2; boolean flag = false; } class WaterIn extends Thread{ Pool pool = new Pool(); int temp = 0; //构造器 public Wat

iOS开发多线程-线程间通讯

一.NSThread 线程间的通讯 1 - (void)demoAboutNSThread 2 { 3 NSLog(@"demoAboutNSThread %@", [NSThread currentThread]); 4 NSThread * thread = [[NSThread alloc] initWithTarget:self selector:@selector(longTimeOperation) object:nil]; 5 [thread start]; 6 } 7

Java多线程之死锁与线程间通信简单案例

死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源:我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行的时候,A还需要LockB进行下面的操作,这个时候b资源在被B线程操作,刚好被上了锁LockB,假如此时线程B刚好释放了LockB则没有问题,但没有释放LockB锁的时候,线程A和B形成了对LockB锁资源的争夺,从而造成阻塞,形成死锁:具体其死锁代码如下:

多线程间的通讯之等待唤醒机制

线程间的通讯: 事实上就是多个线程在操作同一个资源. 可是操作动作不同 样例: 需求:模拟简单卖票系统(输入一个人.紧接着输出一个人) class Res { String name; String sex; } class Input implements Runnable { private Res r; private int t=0; Input(Res r) { this.r=r; } public void run() { while(true) { if(t==1) { r.nam

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是class对象 我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢? 我们做一个这样的小实验,我们给show方法加上static关键字去修饰 private static synchronized void show() { if (tick > 0) { try { Thread