• 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的作用的。

  • 相关阅读:
    StopAllSounds
    GotoAndPlay
    区间(interval)
    因数(factor)
    [HAOI2009]逆序对数列
    生物分子gene
    数轴line
    [SCOI2008]配对
    精力(power)
    bzoj4987: Tree(树形dp)
  • 原文地址:https://www.cnblogs.com/sheeva/p/5020569.html
Copyright © 2020-2023  润新知