zoukankan      html  css  js  c++  java
  • Java中的线程--线程的互斥与同步通信

      Java中的线程之前也提到过,但是还是想再详细的学习一下,跟着张孝祥老师,系统的再学习一下。

    一、线程中的互斥

    线程安全中的问题解释:线程安全问题可以用银行中的转账

    例题描述:

    线程A与线程B分别访问同一个对象的方法,这样就会存在线程安全的问题,方法的作用是打印出字符串中的每一个字符,方法如下:

    1 public void output(String name) {
    2   int len = name.length();
    3   for (int i = 0; i < len; i++) {
    4      System.out.print(name.charAt(i));
    5   }
    6   System.out.println();
    7 }

    线程A和线程B代码如下:(直接写在了init()方法中了)

     1     private void init() {
     2         outputer outputer = new outputer();
     3         new Thread(new Runnable() {
     4             @Override
     5             public void run() {
     6                 while (true) {
     7                     try {
     8                         Thread.sleep(10);
     9                     } catch (InterruptedException e) {
    10                         e.printStackTrace();
    11                     }
    12                     outputer.output("songshengchao");
    13                 }
    14             }
    15         }).start();
    16 
    17         new Thread(new Runnable() {
    18             @Override
    19             public void run() {
    20                 while (true) {
    21                     try {
    22                         Thread.sleep(10);
    23                     } catch (InterruptedException e) {
    24                         e.printStackTrace();
    25                     }
    26                     outputer.output("songxiaochao");
    27                 }
    28             }
    29         }).start();
    30     }

    测试一下,肯定会出现线程不安全的问题,这是母庸质疑的事实,测试代码如下:

    1     public static void main(String[] args) {
    2         new TraditionalThreadSynchronized().init();
    3     }

    三种解决办法,代码如下:

     1 public class outputer {
     2         public void output(String name) {
     3             int len = name.length();
     4             synchronized (this) { // 传进来当前调用方法的对象,要求线程用的是同一个对象
     5             //synchronized (outputer.class) { // 这样和outputer3方法达到线程互斥
     6                 for (int i = 0; i < len; i++) {
     7                     System.out.print(name.charAt(i));
     8                 }
     9                 System.out.println();
    10             }
    11         }
    12 
    13         // 方法上的锁对象用的就是this当前对象
    14         public synchronized void output2(String name) {
    15             int len = name.length();
    16             for (int i = 0; i < len; i++) {
    17                 System.out.print(name.charAt(i));
    18             }
    19             System.out.println();
    20         }
    21 
    22         // output3 想和output方法达到线程互斥
    23         public static synchronized void output3(String name) {
    24             int len = name.length();
    25             for (int i = 0; i < len; i++) {
    26                 System.out.print(name.charAt(i));
    27             }
    28             System.out.println();
    29         }
    30     }

    注意:至于第三种静态的synchronized方法,在和第一个方法共用时,第一种方法一定是用当前对象的class字节码文件,才能确保两个方法用的同一个对象。

     二、线程互斥与通信的经典面试题

    面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在主线程循环100次,如此循环50次,程序如何写???

    经验之谈,设计思想,设计思路:

    要用到共同数据(包括同步锁)的若干个方法应该归在同一个类上,这种设计正好提现了程序的高内聚与健壮性

    思路:先写主线程与子线程的循环,然后在考虑轮流执行。先考虑循环,代码如下:

     1 public class TraditionalThreadCommunication {
     2 
     3     public static void main(String[] args) {
     4 
     5         new Thread(new Runnable() {
     6 
     7             @Override
     8             public void run() {
     9                 for (int i = 1; i <= 50; i++) {
    10                     synchronized (TraditionalThreadCommunication.class) {
    11                         for (int j = 1; j <= 10; j++) {
    12                             System.out.println("sub thread sequece of" + j + ", loop of " + i);
    13                         }
    14                     }
    15 
    16                 }
    17             }
    18         }).start();
    19 
    20         // 本身main方法就是主线程,直接可以写循环代码
    21         for (int i = 1; i <= 50; i++) {
    22             synchronized (TraditionalThreadCommunication.class) {
    23                 for (int j = 1; j <= 100; j++) {
    24                     System.out.println("main thread sequece of" + j + ", loop of " + i);
    25                 }
    26             }
    27         }
    28 
    29     }
    30 }

    代码优化,用面向对象的思想,将那些代码放到一个公共的类中,然后执行类中的不同方法,优化成一个公共的类,代码如下:

     1 public class Business {
     2 
     3     public synchronized void sub(int i){
     4         for (int j = 1; j <= 10; j++) {
     5             System.out.println("sub thread sequece of" + j + ", loop of " + i);
     6         }
     7     }
     8     
     9     public synchronized void main(int i){
    10         for (int j = 1; j <= 100; j++) {
    11             System.out.println("main thread sequece of" + j + ", loop of " + i);
    12         }
    13     }
    14 }
    15 
    16 ----------------------------------------------------------------------------------------------
    17 
    18 public class TraditionalThreadCommunication {
    19 
    20     
    21     public static void main(String[] args) {
    22         Business business = new Business();
    23         new Thread(new Runnable() {
    24 
    25             @Override
    26             public void run() {
    27                 for (int i = 1; i <= 50; i++) {
    28                     business.sub(i);
    29                 }
    30             }
    31         }).start();
    32 
    33         // 本身main方法就是主线程,直接可以写循环代码
    34         for (int i = 1; i <= 50; i++) {
    35             business.main(i);
    36         }
    37 
    38     }
    39 
    40 }

    最终的完整代码如下(详细注释):

     1 public class Business {
     2 
     3     // 是否是子线程执行 默认子线程先执行
     4     private boolean bShouldSub = true;
     5 
     6     public synchronized void sub(int i) {
     7         // 不是子线程应该执行 让给主线程 子线程执行等待的方法
     8         while (!bShouldSub) {
     9             try {
    10                 this.wait();
    11             } catch (InterruptedException e) {
    12                 e.printStackTrace();
    13             }
    14         }
    15         for (int j = 1; j <= 10; j++) {
    16             System.out.println("sub thread sequece of" + j + ", loop of " + i);
    17         }
    18         // 子线程执行完毕后 让给主线程执行
    19         bShouldSub = false;
    20         // 唤醒主线程
    21         this.notify();
    22     }
    23 
    24     public synchronized void main(int i) {
    25         // 是子线程应该执行 让给子线程执行 主线程执行等待的方法
    26         while (bShouldSub) {
    27             try {
    28                 this.wait();
    29             } catch (InterruptedException e) {
    30                 e.printStackTrace();
    31             }
    32         }
    33         for (int j = 1; j <= 100; j++) {
    34             System.out.println("main thread sequece of" + j + ", loop of " + i);
    35         }
    36         // 主线程执行费完毕后 交给子线程执行
    37         bShouldSub = true;
    38         // 唤醒子线程
    39         this.notify();
    40     }
    41 }
    42 ------------------------------------------------------------------------------------------------
    43 
    44 public class TraditionalThreadCommunication {
    45 
    46     public static void main(String[] args) {
    47         Business business = new Business();
    48         new Thread(new Runnable() {
    49 
    50             @Override
    51             public void run() {
    52                 for (int i = 1; i <= 50; i++) {
    53                     business.sub(i);
    54                 }
    55             }
    56         }).start();
    57 
    58         // 本身main方法就是主线程,直接可以写循环代码
    59         for (int i = 1; i <= 50; i++) {
    60             business.main(i);
    61         }
    62 
    63     }
    64 
    65 }
  • 相关阅读:
    python ORM的使用
    python写入mysql
    远程连接不上centos的mysql的解决方法
    linux上mysql的安装
    缓存模块redis
    topic模式下的收发
    direct模式下的收发
    广播模式下的生产者与消费者fanout模式
    [HNOI2008]玩具装箱TOY
    [NOI2009]二叉查找树
  • 原文地址:https://www.cnblogs.com/ssh-html/p/10940790.html
Copyright © 2011-2022 走看看