Java单线程多实例和多线程多实例

  最近写了一个程序,是采用多线程往redis里面写入数据,想统计一下一共写了多少条数据,于是用了一个static的全局变量count来累加,这块代码抽象出来就是这样的:

 1 public class MultiThread implements Runnable {
 2     private String name;
 3     private static Integer count = 0;
 4 
 5     public MultiThread() {
 6     }
 7 
 8     public MultiThread(String name) {
 9         this.name = name;
10     }
11 
12     public void run() {
13         for (int i = 0; i < 5; i++) {
14             //模拟写入redis的IO操作消耗时间
15             try {
16                 Thread.sleep(200);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             
21             //累加写入次数
22             count++;
23             System.out.println(name + "运行        " + i + "    写入条数:" + count);
24         }
25     }
26     
27     public static void main(String[] args) {
28          for (int i = 0; i < 100; i++) {
29              new Thread(new MultiThread("Thread"+i)).start();
30          }
31     }
32 }

启动了100个线程,每个线程写入5次,预计结果应该是500,但是实际结果是这样的:

分析了原因,应该是因为count++不是原子操作,这句代码实际上是执行了3步操作:1,获取类变量count值。2,count+1。3,将count+1后的结果赋值给类变量count。在这3步中间都有可能中断执行其他线程。这样比如线程1先获取了count=0,这时候切换到线程2,线程2获取了count=0,然后又切换到线程1,线程1执行count++=1并修改了类变量count=1,之后又切换到线程2,线程2对之前它获取到的count=0执行count++=1并修改类变量count=1。问题出现了,明明有两个线程对count累加了两次,但是由于count没有加锁,最终类变量只加了1。

根据分析修改程序成下面这样,给count加了同步,将上面代码中第22行的"count++"改为了:

1 synchronized (count) {
2   count++;
3}

这次运行前两次都正常显示了500,但是多运行几次发现个别时候仍然有问题:

再次分析原因,分析不出来了,开始各种修改各种试,终于成功试验出了正确代码,将count++移到外面,封装到类的静态同步方法里:

 1 public class MultiThread implements Runnable {
 2     private String name;
 3     private static Integer count = 0;
 4 
 5     public MultiThread() {
 6     }
 7 
 8     public MultiThread(String name) {
 9         this.name = name;
10     }
11 
12     public void run() {
13         for (int i = 0; i < 5; i++) {
14             //模拟写入redis的IO操作消耗时间
15             try {
16                 Thread.sleep(200);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20             
21             //累加写入次数
22             countPlus();
23             System.out.println(name + "运行        " + i + "    写入条数:" + count);
24         }
25     }
26     
27     private static synchronized void countPlus(){
28         count++;
29     }
30     
31     
32     public static void main(String[] args) {
33          for (int i = 0; i < 100; i++) {
34              new Thread(new MultiThread("Thread"+i)).start();
35          }
36     }
37 }

这次运行多次结果均是正常的,为了确保结果正确,又把线程数改为1000试验了多次,结果也是正确的(5000,不过要好好找找了,因为countPlus()和sysout在多个线程里会交错执行,这个5000不一定会出现在什么位置...从最后一行往前找吧...)。

看着这个运行结果,基本能猜到原因了,原因就出在这一句:

1 new Thread(new MultiThread("Thread"+i)).start();

这里为每个线程new了一个对象,所以之前的

1 synchronized (count) {
2     count++;
3 }

的作用范围是同一个对象的多个线程,也就是说它能够确保Thread1对象的多个线程访问count的时候是同步的,而实际上我们是多线程多实例,每个线程都对应一个不同的对象,所以这句代码实际上是不能起到同步count的作用的。

posted @ 2016-04-05 16:50  叫我家宝  阅读(9217)  评论(3编辑  收藏  举报