多线程学习笔记(四)之线程间通信---等待唤醒机制

Bertha 。 2022-06-16 01:18 259阅读 0赞

线程间通信的一个demo

线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。线程间通信,即多个线程在处理同一资源,但是任务却不同。例如我们现在有两个任务,分别是Input信息和Output信息,用之前(一)到(三)提到的synchronized+锁的方法解决的代码如下(加锁(同步)的问题需要考虑的本质因素,一个锁下是否有多个线程,即多个线程需要持有同一个锁对象),当执行完同步代码块时会释放持有的锁:

  1. class Resource{
  2. String name;
  3. String sex;
  4. }
  5. //输入
  6. class Input implements Runnable{
  7. Resource r;
  8. Input(Resource r){
  9. this.r = r;
  10. }
  11. public void run(){
  12. int x = 0;
  13. while (true){
  14. synchronized (r){
  15. if (x==0){
  16. r.name = "mike";
  17. r.sex = "male";
  18. }else {
  19. r.name = "丽丽";
  20. r.sex = "女";
  21. }
  22. }
  23. x = (x+1)%2;
  24. }
  25. }
  26. }
  27. //输出
  28. class Output implements Runnable{
  29. Resource r;
  30. Output(Resource r){
  31. this.r = r;
  32. }
  33. public void run(){
  34. while (true){
  35. synchronized (r){
  36. System.out.println(r.name+"......"+r.sex);
  37. }
  38. }
  39. }
  40. }
  41. public class ThreadCommunication {
  42. public static void main(String[] args){
  43. //创建资源
  44. Resource r = new Resource();
  45. //创建任务
  46. Input in = new Input(r);
  47. Output out = new Output(r);
  48. //创建线程,即执行路径
  49. Thread t1 = new Thread(in);
  50. Thread t2 = new Thread(out);
  51. //开启线程
  52. t1.start();
  53. t2.start();
  54. }
  55. }

这里写图片描述

我们可以通过结果看到线程的安全问题已经得到解决,但是由于cpu的切换是随机的,因此这种方法不能满足“一个Input,一个Output”的需求。

等待唤醒机制

 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。Object类中相关的方法有两个notify方法和三个wait方法。这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。

这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程,
例如持有A锁的线程wait(),持有B锁的线程中进行notify()是没有用的

为什么操作线程的方法wait,notify,notifyAll定义在了Object类中:
因为这些方法是监视器的方法,监视器其实就可以看作是锁,监控线程的运行状态,锁可以是任意的对象,
任意的对象都可以调用的方法一定是在Object类中

wait()方法

让线程处于冻结状态,释放cpu的执行权和执行资格,被wait的线程会被存储到线程池中。线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中

wait()与sleep()方法的一个对比
当线程调用了wait()方法时,它会释放掉对象的锁。另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。

notify()方法

notify()方法会唤醒一个等待当前对象的锁的线程。

如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。

被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。

被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。

notify()方法应该是被拥有对象的锁的线程所调用。换句话说,和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。

一个线程变为一个对象的锁的拥有者是通过下列三种方法:

1.执行这个对象的synchronized实例方法。

2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。

3.对于Class类的对象,执行那个类的synchronized、static方法。

notifyAll()方法

唤醒线程池中的所有线程,使其处于运行状态或者临时阻塞状态,即使线程具备了执行资格

实例

  1. class Resource{
  2. String name;
  3. String sex;
  4. boolean flag = false;
  5. }
  6. //输入
  7. class Input implements Runnable{
  8. Resource r;
  9. Input(Resource r){
  10. this.r = r;
  11. }
  12. public void run(){
  13. int x = 0;
  14. while (true){
  15. synchronized (r){
  16. if (r.flag){
  17. try{
  18. r.wait();//r锁调用wait
  19. }catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. if (x==0){
  24. r.name = "mike";
  25. r.sex = "male";
  26. }else {
  27. r.name = "丽丽";
  28. r.sex = "女";
  29. }
  30. r.flag = true;
  31. r.notify();//若此时没有等待线程,进行空唤醒也是可以的
  32. }
  33. x = (x+1)%2;
  34. }
  35. }
  36. }
  37. //输出
  38. class Output implements Runnable{
  39. Resource r;
  40. Output(Resource r){
  41. this.r = r;
  42. }
  43. public void run(){
  44. while (true){
  45. synchronized (r){
  46. if (!r.flag){
  47. try{
  48. r.wait();//r锁调用wait
  49. }catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. System.out.println(r.name+"......"+r.sex);
  54. r.flag = false;
  55. r.notify();//唤醒r线程池中的线程
  56. }
  57. }
  58. }
  59. }
  60. public class ThreadCommunication {
  61. public static void main(String[] args){
  62. //创建资源
  63. Resource r = new Resource();
  64. //创建任务
  65. Input in = new Input(r);
  66. Output out = new Output(r);
  67. //创建线程,即执行路径
  68. Thread t1 = new Thread(in);
  69. Thread t2 = new Thread(out);
  70. //开启线程
  71. t1.start();
  72. t2.start();
  73. }
  74. }

这里写图片描述

实例优化

  1. //修改方向:在资源中加入同步的访问和输出方法
  2. class Resource{
  3. private String name;
  4. private String sex;
  5. boolean flag = false;
  6. //同步函数的锁是this
  7. public synchronized void set(String name,String sex){
  8. if (flag){
  9. try{
  10. this.wait();//r锁调用wait
  11. }catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. this.name = name;
  16. this.sex = sex;
  17. flag = true;
  18. this.notify();
  19. }
  20. public synchronized void out(){
  21. if (!flag){
  22. try{
  23. this.wait();//r锁调用wait
  24. }catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. System.out.println(name+"......"+sex);
  29. flag = false;
  30. this.notify();//唤醒r线程池中的线程
  31. }
  32. }
  33. //输入
  34. class Input implements Runnable{
  35. Resource r;
  36. Input(Resource r){
  37. this.r = r;
  38. }
  39. public void run(){
  40. int x = 0;
  41. while (true){
  42. if (x==0){
  43. r.set("mike","male");
  44. }else {
  45. r.set("丽丽","女");
  46. }
  47. x = (x+1)%2;
  48. }
  49. }
  50. }
  51. //输出
  52. class Output implements Runnable{
  53. Resource r;
  54. Output(Resource r){
  55. this.r = r;
  56. }
  57. public void run(){
  58. while (true){
  59. r.out();
  60. }
  61. }
  62. }
  63. public class ThreadCommunication {
  64. public static void main(String[] args){
  65. //创建资源
  66. Resource r = new Resource();
  67. //创建任务
  68. Input in = new Input(r);
  69. Output out = new Output(r);
  70. //创建线程,即执行路径
  71. Thread t1 = new Thread(in);
  72. Thread t2 = new Thread(out);
  73. //开启线程
  74. t1.start();
  75. t2.start();
  76. }
  77. }

发表评论

表情:
评论列表 (有 0 条评论,259人围观)

还没有评论,来说两句吧...

相关阅读