几个并发编程的例子(篇二)

几个并发编程的例子

在这里讨论一下自己遇到的几个自己感觉比较好的并发编程的例子。如果读者已经完全明白是怎么回事,请略过。

例一:

      先看如下程序,想一下这个程序多长时间结束?

Java代码    收藏代码
  1. import java.util.concurrent.TimeUnit;  
  2. public class Stop {  
  3. private static boolean isStop = false;  
  4. public static void main(String[] args) throws Exception {  
  5.     Thread thread = new Thread(new Runnable(){  
  6.         public void run(){  
  7.             int count = 0;  
  8.             while(!isStop){  
  9.                 count++;  
  10.             }  
  11.             System.out.println("循环结束,此时count值为:" + count);  
  12.             }  
  13.         });  
  14.         thread.start();  
  15.         TimeUnit.SECONDS.sleep(1);  
  16.         isStop = true;  
  17.     }  
  18. }  

 

在我的机器上,这个程序永远停不下来!你也可以在自己的机器上试一下。

这是为什么呢?

原因是:虽然在主线程中修改了isStop的值,但是在线程thread中永远看不到修改后的值,虽然循环在执行,在进行检测。在《Effective Java》中我们可以看到这样的解释,由于没有同步,虚拟机将这个代码:

Java代码    收藏代码
  1. while(!isStop){  
  2.     count++;  
  3. }  

转变成了这样:

Java代码    收藏代码
  1. if(!isStop){  
  2.   while(true){  
  3.     count++;  
  4.     }  
  5. }  

 

    修正这个问题的一种方式是,同步访问isStop这个域,这样程序才会如期停止。在上面的程序中添加如下代码片段即可:

Java代码    收藏代码
  1. private static synchronized void stop(){  
  2.     isStop = true;  
  3. }  
  4.       
  5. private static synchronized boolean stoped(){  
  6.     return isStop;  
  7. }  

 

其实在上面我们可以看到,上面的方法即使没有被同步也是原子的,但是这里为什么要这么做呢,原因只是为了synchronized的通信效果,而不是互斥访问。

 

还一种修改方式更为简洁,只需要将isStop声明为volatile即可,这样上面的同步方法即可略去,volatile可以保证任何一个线程在读取该域的时候都将看到最近被写入的值。

 

例二:

上面提到了volatile,我们可以再看一个使用它的例子,如下:

Java代码    收藏代码
  1. private static volatile int number = 0;  
  2. public static int nextNumber(){  
  3.     return number++;  
  4. }  

    上面这个方法的目的是让每个调用都返回不同的值,但是不要超过int的范围。但是上面的这个方法是无法正常工作的,问题出现在哪呢?问题出现在递增操作符(++)不是原子的。这个操作在number域中执行两项操作,首先读取值,然后再写入新的值。如果一个线程在另一个线程读取旧值和写回新值的这个期间读取这个域,那么两个线程看到的是同一个值,所以会返回相同的值。

     对于这个我们的修改方式就是使用synchronized关键字,这样可以保证不会出现交叉调用的出现,或是使用java.util.concurrent.atomic的一部分来达到效果。

 

     例三:

     下面再看这样一个程序,它最后会保证a,b两个数字相同吗?

Java代码    收藏代码
  1. import java.io.IOException;  
  2. public class MThread{  
  3.     public static void main(String[] args) throws IOException{  
  4.         ShareObject so = new ShareObject();  
  5.         Thread thread1 = new Thread(new ThreadOperation(so,"add"));  
  6.         Thread thread2 = new Thread(new ThreadOperation(so,"sub"));  
  7.         thread1.setDaemon(true);  
  8.         thread2.setDaemon(true);  
  9.         thread1.start();  
  10.         thread2.start();  
  11.         System.out.println("按Enter键结束!");  
  12.         System.in.read();  
  13.         System.out.println("此时的so里面的a,b分别为a="+so.a+"b="+so.b);  
  14.     }  
  15. }  
  16.   
  17. class ThreadOperation implements Runnable{  
  18.     private String operation;  
  19.     private ShareObject so;  
  20.       
  21.     public ThreadOperation(ShareObject so,String oper){  
  22.         this.operation = oper;  
  23.         this.so = so;  
  24.     }  
  25.   
  26.     public void run() {  
  27.         while(true){  
  28.             if(operation.equals("add")){  
  29.                 so.add();  
  30.             }else{  
  31.                 so.sub();  
  32.             }  
  33.         }  
  34.     }  
  35. }  
  36.   
  37. class ShareObject{  
  38.     int a = 100;  
  39.     int b = 100;  
  40.       
  41.     public synchronized void add(){  
  42.         ++a;  
  43.         ++b;  
  44.     }  
  45.       
  46.     public synchronized void sub(){  
  47.         --a;  
  48.         --b;  
  49.     }  
  50. }  

 

     经过我自己的运行,运行出来的a,b的值相差很大,如下所示:

 

输出结果:

 

按Enter键结束!  

此时的so里面的a,b分别为 a=4553016b=4552099

  

    这是为什么呢?我再来一个更迷惑的解决方法,我们可以试着给run()方法里面执行完加或者减操作后添加一个停止很短时间的代码,结果输出就会正确!添加后如下:

Java代码    收藏代码
  1. public void run() {  
  2.     while(true){  
  3.         if(operation.equals("add")){  
  4.             so.add();  
  5.         }else{  
  6.             so.sub();  
  7.         }  
  8.         try {  
  9.             TimeUnit.MILLISECONDS.sleep(1);  
  10.         } catch (InterruptedException e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.     }  
  14. }  
 而这个的输出结果如下,可以保证a,b相同:
输出结果:
按Enter键结束!  

此时的so里面的a,b分别为 a=98b=98
 

     这样结果会输出正确,但是更迷惑了。这些是为什么呢?

     其实根本原因是,在类ShareObject中,域a,b没有设置为private,那么我们就可以直接访问,因此注意:在使用并发时,将域设置为private很重要,否则关键字synchronized不能防止其他线程直接访问域,这样就会产生冲突。所以在上面的里例子中,我们需要将a,b设置为private的,然后通过方法进行访问,那么就可以保证不会出现这种情况。代码修改如下所示,在ShareObject中将a,b设置为private的并添加同步访问a,b的方法,然后在主函数中进行调用。

Java代码    收藏代码
  1. import java.io.IOException;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class MThread{  
  4.     public static void main(String[] args) throws IOException{  
  5.         ShareObject so = new ShareObject();  
  6.         Thread thread1 = new Thread(new ThreadOperation(so,"add"));  
  7.         Thread thread2 = new Thread(new ThreadOperation(so,"sub"));  
  8.         thread1.setDaemon(true);  
  9.         thread2.setDaemon(true);  
  10.         thread1.start();  
  11.         thread2.start();  
  12.         System.out.println("按Enter键结束!");  
  13.         System.in.read();  
  14.         int[] val = so.getValue();  
  15.         System.out.println("此时的so里面的a,b分别为 a="+val[0]+"b="+val[1]);  
  16.     }  
  17. }  
  18.   
  19. class ThreadOperation implements Runnable{  
  20.     private String operation;  
  21.     private ShareObject so;  
  22.       
  23.     public ThreadOperation(ShareObject so,String oper){  
  24.         this.operation = oper;  
  25.         this.so = so;  
  26.     }  
  27.   
  28.     public void run() {  
  29.         while(true){  
  30.             if(operation.equals("add")){  
  31.                 so.add();  
  32.             }else{  
  33.                 so.sub();  
  34.             }  
  35.             try {  
  36.                 TimeUnit.MILLISECONDS.sleep(1);  
  37.             } catch (InterruptedException e) {  
  38.                 e.printStackTrace();  
  39.             }  
  40.   
  41.         }  
  42.     }  
  43. }  
  44.   
  45. class ShareObject{  
  46.     private int a = 100;  
  47.     private int b = 100;  
  48.       
  49.     public synchronized int[] getValue(){  
  50.         return new int[]{a,b};  
  51.     }  
  52.       
  53.     public synchronized void add(){  
  54.         ++a;  
  55.         ++b;  
  56.     }  
  57.       
  58.     public synchronized void sub(){  
  59.         --a;  
  60.         --b;  
  61.     }  
  62. }  

 这下得到输出结果如下:

输出结果
按Enter键结束!  

此时的so里面的a,b分别为 a=96b=96

 

       并发编程真的博大精深,继续研究!

 

未完,待续!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值