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

 public class MultiThread implements Runnable {
private String name;
private static Integer count = 0; public MultiThread() {
} public MultiThread(String name) {
this.name = name;
} public void run() {
for (int i = 0; i < 5; i++) {
//模拟写入redis的IO操作消耗时间
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} //累加写入次数
count++;
System.out.println(name + "运行 " + i + " 写入条数:" + count);
}
} public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new MultiThread("Thread"+i)).start();
}
}
}

启动了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++"改为了:

 synchronized (count) {
  count++;
}

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

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

 public class MultiThread implements Runnable {
private String name;
private static Integer count = 0; public MultiThread() {
} public MultiThread(String name) {
this.name = name;
} public void run() {
for (int i = 0; i < 5; i++) {
//模拟写入redis的IO操作消耗时间
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} //累加写入次数
countPlus();
System.out.println(name + "运行 " + i + " 写入条数:" + count);
}
} private static synchronized void countPlus(){
count++;
} public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new MultiThread("Thread"+i)).start();
}
}
}

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

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

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

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

 synchronized (count) {
count++;
}

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

最新文章

  1. git log命令全解析,打log还能这么随心所欲!
  2. Autodesk 最新开发技术研讨会-北京-上海-武汉-成都-西安-PPT下载
  3. PDA手持移动POS销售开单软件(网络版)、PDA手持设备小票机
  4. 关于UID和GID的创建、修改、删除;简要举例
  5. css构造块级元素
  6. &lt;marquee&gt;属性详解
  7. Python15-day1课后作业
  8. hdu 4496 (并差集)
  9. EventLog实现事件日志操作
  10. Linux sed命令删除指定行
  11. Strange Country II 暴力dfs
  12. FTP下载帮助类
  13. 语音激活检测(VAD)--前向神经网络方法(Alex)
  14. Web 小案例 -- 网上书城(三)
  15. day16——函数式编程和内置函数
  16. Mysql出现(10061)错误提示的暴力解决办法
  17. OkHttp 入门篇
  18. [洛谷P3948]数据结构 题解(差分)
  19. Shiro笔记(一)基本概念
  20. BZOJ3600:没有人的算术

热门文章

  1. BZOJ5322:[JXOI2018]排序问题——题解
  2. BZOJ2597 [Wc2007]剪刀石头布 【费用流】
  3. POJ.2142 The Balance (拓展欧几里得)
  4. LibreOJ #6221. 幂数 !(数论+dfs+剪枝)
  5. 使用自己的数据集训练和测试&quot;caffenet&quot;
  6. 【题解】Radio stations Codeforces 762E CDQ分治
  7. ssm项目,web容器无法初始化项目
  8. 第一章 深入web请求过程
  9. 实体框架(Entity Framework)快速入门--实例篇
  10. 理解JavaScript的prototype和__proto__