首先对于这样的问提,要考虑的就是: 若花园中的任意两个大门在同一时刻分别进入两个人,在花园总计数器上到底是加2还是加4?
我们肯定会不假思索的回答,这TM还要问,肯定是加4啊,是2不就完了嘛. 但是计算机往往就是这样,对人脑来说很简单的操作,对于计算机来说却要大费周折. 结果是:有可能是2(如果在没有任何并发操作的情况下)
有的同学会问,为啥呢? 因为在java中 简单的读取和写入操作 不是原子性操作,也就是说 :获取总计数器的值、将值+2 是两个操作。 所以就会出现 一个门获取总计数器的值0(初始值0)时,还没有完成对其+2的操作,另一个门也获取了总计数器的值,但此时总计数的值还是0,并对其进行+2操作。所以当两个们在完成取指和赋值的操作后,我们看到的总计数器的值却是2。
所以为了不让 有可能是2, 我们必须对总计数器取值 和 赋值操作进行同步,也就是我们常见的synchronized 关键字、或者使用显示的 Lock 类 对所有与总计数器相关的操作进行同步。
以下是我写的一个渣渣代码,仅供参考。
先附上无并发控制的代码和运行结果(注意: 此处没有对公园总人数加上synchronized)
package com.sanmina.threadDemo.synchro;/** * TotalCount.java */import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;/** * CountMachine 花园总人数计数器 * * @author mercy_yang * */class CountMachine { /* totalCount 总计数器的人数 */ private static int totalCount; public int getTotalCount() { return totalCount; } public void incrementTotal(int person) { totalCount = totalCount + person; } @Override public String toString() { return "TOTAL: " + totalCount; }}/** * Doors 各个门统计本门计入人数 * * @author mercy_yang * */class Doors implements Runnable { /* countMachine 花园总人数计数器 */ private CountMachine countMachine; /* count 这个门进入的总人数 */ private int count = 0; /* entryPerson 这个门每次的进入的人数 */ private int entryPerson; /* id 门的标识 */ private int id; /* rand 产生随机的入门人数 */ private static Random rand = new Random(47); /* doors 统计所有门 */ private static Listdoors = new ArrayList (); /* cancel 结束标识,此处必须是volatlie 否则会出现意向不到的结果 */ private volatile static boolean cancel = false; public static void canceled() { cancel = true; } public Doors(int id, CountMachine countMachine) { this.id = id; this.countMachine = countMachine; doors.add(this); } public int getEntry() { return entryPerson; } public synchronized void addCount() { count = count + entryPerson; } public synchronized int getTotal() { return count; } public static int getDoorsCount() { int sum = 0; for (Doors door : doors) { System.out.println(door); sum += door.getTotal(); } return sum; } /** * 统计每次本门入院的人数 并 在总人数增加 */ @Override public void run() { while (!cancel) { entryPerson = rand.nextInt(5); synchronized (this) { Thread.yield(); if (entryPerson != 0) { addCount(); countMachine.incrementTotal(entryPerson); System.out.println("本次进入 " + entryPerson + "个人 " + this); } } try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread.interrupted(); } System.out.println("Stopping " + this); } @Override public String toString() { return "#Door" + id + ": " + count + " TOTAL: " + countMachine.getTotalCount(); }}public class TotalCount { /* countMachine 唯一的花园总人数计数器 */ private static CountMachine countMachine = new CountMachine(); public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newCachedThreadPool(); /* 定义5个大门 */ for (int i = 0; i < 5; i++) { exec.execute(new Doors(i, countMachine)); } TimeUnit.SECONDS.sleep(1); Doors.canceled(); exec.shutdown(); // 结束 /* 判断所有线程是否运行结束 */ while (Thread.activeCount() > 1) { Thread.yield(); } System.out.println("*********"); System.out.println("所有门总人数和"+Doors.getDoorsCount()); System.out.println("花园总人数: " + countMachine.getTotalCount()); }}
运行结果
本次进入 3个人 #Door0: 3 TOTAL: 3本次进入 1个人 #Door3: 1 TOTAL: 7本次进入 3个人 #Door2: 3 TOTAL: 6本次进入 1个人 #Door4: 1 TOTAL: 8本次进入 4个人 #Door1: 4 TOTAL: 12本次进入 3个人 #Door0: 6 TOTAL: 15本次进入 2个人 #Door3: 3 TOTAL: 17本次进入 2个人 #Door4: 3 TOTAL: 19本次进入 3个人 #Door1: 7 TOTAL: 22本次进入 3个人 #Door0: 9 TOTAL: 25本次进入 1个人 #Door2: 4 TOTAL: 26..............*********#Door0: 22 TOTAL: 98#Door1: 19 TOTAL: 98#Door2: 20 TOTAL: 98#Door3: 18 TOTAL: 98#Door4: 20 TOTAL: 98所有门总人数和:99花园总人数: 98
可以看到, 我对各个们进入的人数进行了并发控制,没有对花园总人数的操作进行并发控制。
所以结果中的花园总人数是98 而 所有们加起来的人数是 99 。明显出现了差异。(注:各人运行的结果可能不同)
现在我为CountMachine 类中的 count 静态变量(此处必须是静态变量) 加上synchronizedr如下(其他不变)
class CountMachine { /* totalCount 总计数器的人数 */ private static int totalCount; public synchronized int getTotalCount() { return totalCount; } public synchronized void incrementTotal(int person) { totalCount = totalCount + person; } @Override public String toString() { return "TOTAL: " + totalCount; }}
运行结果
本次进入 3个人 #Door0: 3 TOTAL: 3本次进入 3个人 #Door2: 3 TOTAL: 6本次进入 1个人 #Door3: 1 TOTAL: 7本次进入 1个人 #Door4: 1 TOTAL: 8本次进入 4个人 #Door0: 7 TOTAL: 12本次进入 3个人 #Door1: 3 TOTAL: 15............*********#Door0: 27 TOTAL: 99#Door1: 12 TOTAL: 99#Door2: 20 TOTAL: 99#Door3: 16 TOTAL: 99#Door4: 24 TOTAL: 99所有门人数总和: 99花园总人数: 99
可以看到在 CountMachine 类中的count的 getTotalCount()方法和incrementTotal()方法上加上 synchronized 后 总数就对上了。(可以多次运行,查看总数是否会对不上),当然博主可以保证不会。嘻嘻...
大家可以自己尝试使用Lock类