package lime.tij._021._004._001.me;
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;
/**
* @Author : Liangmy
* @Description :
* @Date : Created in 2020/2/17 下午8:01
* @Modified By :
*/
class Count {
/**
* count在多线程中是共享资源,对count域的读写操作都需要同步处理
*/
private int count = 0;
private Random rand = new Random(47);
/**
* 2* Count.increment()和Count.value()都是synchronized的,用来控制对count域的访问
* 3* increment()方法使用了Random对象,目的是在从把count读取到temp中,到递增temp并将其存储回count的这段时间里,有大约一半的时间产生让步。
*/
public synchronized int increment() {
int temp = count;
if (rand.nextBoolean()) {
Thread.yield();
}
return (count = ++temp);
}
/**
* 2* Count.increment()和Count.value()都是synchronized的,用来控制对count域的访问
*/
public synchronized int value() {
return count;
}
}
class Entrance implements Runnable {
private Random rand = new Random(47);
/**
* 1* 这里使用单个的Count对象来跟踪花园参观者的主计数值,并且将其当作Entrance类中的一个静态域进行存储。
*/
private static Count count = new Count();
private static List<Entrance> entrances = new ArrayList<>();
/**
* 每个Entrance任务都维护着一个本地值number,它包含通过某个特定入口进入的参观者的数量。
* 这提供了对count对象的双重检查,以确保其记录的参观者数量是正确的。
*/
private int number = 0;
private final int id;
/**
* 因为Entrance.canceled是一个volatile布尔标志,而它只会被读取和赋值(不会与其他域组合在一起被读取),所以不需要同步对其的访问,就可以安全地操作它。
*/
private volatile static boolean canceled = false;
public static void cancel() {
canceled = true;
}
public Entrance(int id) {
this.id = id;
entrances.add(this);
}
/**
* Entrance.run()只是递增number和count对象,然后休眠100毫秒
*
* ???????共享资源是Count.count,为什么number也需要同步?number是属于各个实例对象的,并不参与多线程操作,应该不用同步也能正确吧?!
*/
@Override
public void run() {
while (!canceled) {
// synchronized (this) {
// number++;
// }
number++;
if (rand.nextBoolean()) {
Thread.yield();
}
number++;
System.out.println(this + " Total : " + count.increment());
System.out.println(this + " Total : " + count.increment());
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
System.out.println("sleep interrupted");
}
}
System.out.println("Stopping " + this);
}
/**
* ???????共享资源是Count.count,为什么number也需要同步?number是属于各个实例对象的,并不参与多线程操作,应该不用同步也能正确吧?!
*/
public int getValue() {
// public synchronized int getValue() {
return number;
}
@Override
public String toString() {
return "Entrance " + id + " : " + getValue();
}
public static int getTotalCount() {
return count.value();
}
public static int sumEntrances() {
int sum = 0;
for (Entrance entrance : entrances) {
sum += entrance.getValue();
}
return sum;
}
}
/**
* @author liangmy
*/
public class OrnamentalGarden {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new Entrance(i));
}
TimeUnit.SECONDS.sleep(3);
Entrance.cancel();
executorService.shutdown();
if (!executorService.awaitTermination(250, TimeUnit.MILLISECONDS)) {
System.out.println("Some tasks were not terminated!");
}
System.out.println("Total : " + Entrance.getTotalCount());
System.out.println("Sum of Entrances : " + Entrance.sumEntrances());
}
}
共享资源是Count.count,为什么number也需要同步?number是属于各个实例对象的,并不参与多线程操作,应该不用同步也能正确吧?!
什么道理?