java多线程与单线程_Java单线程多实例和多线程多实例

本文探讨了一段使用多线程往Redis写入数据的Java代码中,全局静态变量count累加出现并发问题的案例。作者通过深入剖析线程同步原理,展示了为何原始的count++操作导致计数错误,并逐步修复过程,最终确定了正确的同步方法——使用静态同步方法countPlus()。
摘要由CSDN通过智能技术生成

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

1 public class MultiThread implementsRunnable {2 privateString name;3 private static Integer count = 0;4

5 publicMultiThread() {6 }7

8 publicMultiThread(String name) {9 this.name =name;10 }11

12 public voidrun() {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 voidmain(String[] args) {28 for (int i = 0; i < 100; i++) {29 new Thread(new MultiThread("Thread"+i)).start();30 }31 }32 }

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

ca623ec8f7a29d021cb1452cfca979f4.png

分析了原因,应该是因为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,但是多运行几次发现个别时候仍然有问题:

54df96f119d3dba5d8e97bdacc3abbac.png

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

1 public class MultiThread implementsRunnable {2 privateString name;3 private static Integer count = 0;4

5 publicMultiThread() {6 }7

8 publicMultiThread(String name) {9 this.name =name;10 }11

12 public voidrun() {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 voidcountPlus(){28 count++;29 }30

31

32 public static voidmain(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的作用的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值