`
wojiaolongyinong
  • 浏览: 74752 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

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

    博客分类:
  • Java
阅读更多

几个并发编程的例子

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

例一:

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

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

 

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

 

未完,待续!

4
4
分享到:
评论
9 楼 wojiaolongyinong 2013-10-08  
comrd 写道
例1没改之前可以停下来,改了反而停不下来,不知道楼主有没有运行过代码。。

代码我运行过,是停不下来的。
8 楼 wojiaolongyinong 2013-10-08  
sswh 写道
例1的代码在我的电脑上执行是会停止的。


我运行过,不会停下来的,我的是JDK1.7,但不应该是这个问题。
7 楼 AKka 2013-10-08  
sswh 写道
例1的代码在我的电脑上执行是会停止的。


我的也是会停止,JDK1.6
6 楼 comrd 2013-10-08  
例1没改之前可以停下来,改了反而停不下来,不知道楼主有没有运行过代码。。
5 楼 sswh 2013-10-08  
例1的代码在我的电脑上执行是会停止的。

4 楼 wojiaolongyinong 2013-10-08  
weiqiang.yang 写道
第三个感觉是变量可见性的问题

System.out.println("此时的so里面的a,b分别为a="+so.a+"b="+so.b);

直接访问字段无法保证其他线程的修改在该线程可见

1. 修改字段为volatile
2. 增加同步getValue()方法访问a/b

在这里,首先虽然改变变量的是后台线程,但是在主线程输出那里后台线程仍在运行,那么直接访问变量,这里就会出现同步问题,虽然线程进去了加锁方法,但主线程直接访问了变量,所以会出现冲突,那么同步不可避免,使用加锁的getValue()还是比较好。
3 楼 weiqiang.yang 2013-10-08  
第三个感觉是变量可见性的问题

System.out.println("此时的so里面的a,b分别为a="+so.a+"b="+so.b);

直接访问字段无法保证其他线程的修改在该线程可见

1. 修改字段为volatile
2. 增加同步getValue()方法访问a/b
2 楼 wojiaolongyinong 2013-10-07  
laizhaoshi 写道
第三个例子很迷惑,没看到其他线程直接访问ShareObject成员变量并修改他们啊...

在主线程里面直接调用,问题出在这里,这里没有达到同步。
1 楼 laizhaoshi 2013-10-07  
第三个例子很迷惑,没看到其他线程直接访问ShareObject成员变量并修改他们啊...

相关推荐

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第二阶段05讲、一个解释volatile关键字作用最好的例子.mp4 │ 高并发编程第二阶段06讲、Java内存模型以及CPU缓存不一致问题的引入.mp4 │ 高并发编程第二阶段07讲、CPU以及CPU缓存的结构,解决高速...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第二阶段05讲、一个解释volatile关键字作用最好的例子.mp4 │ 高并发编程第二阶段06讲、Java内存模型以及CPU缓存不一致问题的引入.mp4 │ 高并发编程第二阶段07讲、CPU以及CPU缓存的结构,解决高速...

    《Java并发编程实战》的高清完整PDF版

    本书主要涵盖以下几个关键知识点: 1. **线程基础**:首先,你需要了解线程的基本概念,包括如何创建和管理线程。在Java中,可以通过实现`Runnable`接口或继承`Thread`类来创建线程。同时,Java还提供了`...

    《Java7并发编程实战手册》书中实例代码

    在Java 7中,并发编程得到了显著的提升,主要体现在以下几个方面: 1. **Fork/Join框架**:Java 7引入了Fork/Join框架,这是一个用于并行执行任务的高级框架,基于工作窃取算法。它将大任务拆分为小任务,并在多个...

    java并发编程实践

    在Java并发编程中,主要涉及以下几个关键知识点: 1. **线程与进程**:首先,理解线程和进程的基本概念是至关重要的。线程是程序执行的最小单位,而进程则是资源分配的基本单位。Java通过Thread类和Runnable接口来...

    并发编程的几个问题解析

    在并发编程中,有几个关键问题值得深入探讨。首先,我们来看一下这些具体的问题: 1. **工作线程数设置**:并非工作线程数设置得越大越好。理想情况下,线程数应该根据系统的硬件资源(如CPU核心数)以及任务特性来...

    Python高性能网络编程并发框架研究.pdf

    本文将对以下几个知识点进行详细阐述: 1. 网络编程基本概念 网络编程涉及不同计算机或网络设备间通过网络进行数据交换的过程。在这个过程中,网络通信模型的设计至关重要,因为不同的模型会影响服务的并发性能和...

    并发编程_模式.pdf

    并发编程中的同步模式之一是保护性暂停模式,该模式用于确保一个线程在等待另一个线程完成某项任务后才能继续执行。这种模式下的线程关联同一个GuardedObject,它能够保护线程间的同步,并且能够处理线程间的通信。 ...

    go并发编程

    ### Go 并发编程知识点详解 #### 一、并发编程概览 在现代软件开发中,**并发编程**已经成为了一项必备技能。特别是在高负载、高性能的应用场景下,利用好并发能够极大地提升程序的执行效率和响应速度。Golang...

    Java并发编程的艺术&源码

    在Java并发编程中,有几个核心的知识点是必须要掌握的: 1. **线程**:Java通过`Thread`类来创建和管理线程,它是并发的基础。线程允许程序在同一时刻执行多个任务,提高系统资源利用率。 2. **同步机制**:为了...

    Java并发编程实战(中文版).7z

    本书的核心内容涵盖了以下几个方面: 1. **线程基础**:讲解了Java中的Thread类和Runnable接口,以及如何创建和启动线程。此外,还介绍了线程的生命周期和状态,包括新建、就绪、运行、阻塞和终止等。 2. **同步...

    java并发编程实战源码-jcip-examples:java并发编程实战

    在这个源码库中,你可以找到以下几个重要的并发编程知识点: 1. **线程安全**:书中的例子会涵盖各种线程安全的实现方式,如synchronized关键字、volatile变量、Atomic类等。理解这些机制能帮助你确保多线程环境下...

    Java并发编程(23)并发新特性-信号量Semaphor

    `Semaphore`类主要包含以下几个方法: 1. `acquire()`: 这个方法用于获取一个许可。如果没有可用的许可,线程会被阻塞直到有一个许可被释放。 2. `tryAcquire()`: 尝试获取一个许可,但不会阻塞。如果当前没有可用的...

    C#高效率SOCKET并发端口例子

    总之,“C#高效率SOCKET并发端口例子”是一个实践性强、涉及面广的学习资料,涵盖了网络编程中的基础概念和高级技巧,对于提升C#网络编程能力大有裨益。通过学习和理解这个示例,开发者可以更好地应对各种网络应用...

    Django实现并发处理的例子

    总的来说,这个例子涵盖了以下几个关键知识点: 1. Django项目的创建和结构。 2. Django中的视图、模型和URL配置。 3. Python线程和并发处理。 4. Linux环境下的并发支持。 5. 处理临界资源的锁机制。 6. Django的...

    Java并发编程(22)并发新特性-障碍器CyclicBa

    CyclicBarrier的主要方法有以下几个: 1. `CyclicBarrier(int parties)`: 构造一个CyclicBarrier,参数parties指定了需要等待的线程数量。当所有线程都到达屏障点时,屏障才会放行。 2. `void await() throws ...

    C#并发编程入门教程之概述

    写在前面 并发编程一直都存在,只不过过去的很长时间里,比较...关于并发编程的几个误解 误解一:并发编程就是多线程 实际上多线只是并发编程的一中形式,在C#中还有很多更实用、更方便的并发编程技术,包括异步编

    MFC TCP编程的几个代码例子

    7. **多线程应用**:在服务器端,为了处理多个并发连接,通常需要使用多线程。MFC提供了CWinThread类,可以创建并管理线程,确保每个连接都有独立的工作线程处理。 8. **关闭连接**:完成通信后,记得调用Close()...

    详解Python并发编程之从性能角度来初探并发编程

    并发编程 这个系列,我准备了将近一个星期,从知识点梳理,到思考要举哪些例子才能更加让人容易吃透这些知识点。希望呈现出来的效果真能如想象中的那样,对小白也一样的友好。 昨天大致整理了下,这个系列我大概会...

Global site tag (gtag.js) - Google Analytics