跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • Java 编程思想的这个 OrnamentalGarden 例子为什么是这种打印结果?
未分類
3 5 月 2020

Java 编程思想的这个 OrnamentalGarden 例子为什么是这种打印结果?

Java 编程思想的这个 OrnamentalGarden 例子为什么是这种打印结果?

資深大佬 : amiwrong123 15

这是 java 编程思想的一个小例子,大概意思,五个 Runnable 对象会对自己加锁(但对自己加锁,因为没有别人和它本身冲突,所以没有意义),还有个 Count 对象,它也有同步方法,五个线程都会调到它,自然,只有它的同步方法才是有意义的。

import java.util.concurrent.*; import java.util.*; import java.util.concurrent.atomic.AtomicInteger;  import static net.mindview.util.Print.*;  class Count {     private int count = 0;     private Random rand = new Random(47);     // Remove the synchronized keyword to see counting fail:     public synchronized int increment() {         int temp = count;         System.out.println(Thread.currentThread().getName()+" befor yield");         if(rand.nextBoolean()) // Yield half the time             Thread.yield();         System.out.println(Thread.currentThread().getName()+" after yield");         return (count = ++temp);     }     public synchronized int value() { return count; } }  class Entrance implements Runnable {     private static Count count = new Count();     private static List<Entrance> entrances =             new ArrayList<Entrance>();     private int number = 0;     // Doesn't need synchronization to read:     private final int id;     private static volatile boolean canceled = false;     // Atomic operation on a volatile field:     public static void cancel() { canceled = true; }     public Entrance(int id) {         this.id = id;         // Keep this task in a list. Also prevents         // garbage collection of dead tasks:                  entrances.add(this);     }     public void run() {         while(!canceled) {             synchronized(this) {                 ++number;             }             print(this + " Total: " + count.increment());             try {                 TimeUnit.MILLISECONDS.sleep(100);             } catch(InterruptedException e) {                 print("sleep interrupted");             }         }         print("Stopping " + this);     }     public synchronized int getValue() { return number; }     public String toString() {         return "Entrance " + Thread.currentThread().getName() + ": " + getValue();     }     public static int getTotalCount() {         return count.value();     }     public static int sumEntrances() {         int sum = 0;         for(Entrance entrance : entrances)             sum += entrance.getValue();         return sum;     } }  public class OrnamentalGarden {     public static void main(String[] args) throws Exception {         AtomicInteger a = new AtomicInteger();         ExecutorService exec = new ThreadPoolExecutor(0, Integer.MAX_VALUE,                 60L, TimeUnit.SECONDS,                 new SynchronousQueue<Runnable>(),new ThreadFactory(){ public Thread newThread(Runnable r) {             return new Thread(r, "pool_" + a.getAndIncrement());         }});         for(int i = 0; i < 5; i++)             exec.execute(new Entrance(i));         // Run for a while, then stop and collect the data:         TimeUnit.SECONDS.sleep(3);         Entrance.cancel();         exec.shutdown();         if(!exec.awaitTermination(250, TimeUnit.MILLISECONDS))             print("Some tasks were not terminated!");         print("Total: " + Entrance.getTotalCount());         print("Sum of Entrances: " + Entrance.sumEntrances());     } } 

可能好多人看到这里直接略过了,但我看完这个例子确实似懂非懂的。https://paste.ubuntu.com/p/B7GQMcMhv7/ ,这是原版代码,为了看到简单的线程名,稍微改了下。执行完后,打印结果是:

pool_0 befor yield pool_0 after yield pool_2 befor yield pool_2 after yield pool_4 befor yield pool_4 after yield pool_3 befor yield pool_3 after yield pool_1 befor yield pool_1 after yield Entrance pool_0: 1 Total: 1 Entrance pool_3: 1 Total: 4   为啥这里直接是 4 了? Entrance pool_1: 1 Total: 5 Entrance pool_2: 1 Total: 2 Entrance pool_4: 1 Total: 3 pool_0 befor yield pool_0 after yield 

如上,为啥那里直接是 4 了?那个 4 是 count 对象的同步方法,按理说应该接下来打印 2 啊。所以这个程序是想说明,1 过了直接打印 4,是因为线程调度吗?作者想说明什么啊

PS:有时候感觉 Java 编程思想的例子好恶心。。。

大佬有話說 (8)

  • 資深大佬 : hfc

    这就是多线程问题吧,你按线程打印“before/after yield”的顺序去看对应的 Total 值是正确递增的。
    Count 对象的同步方法也没意义,因为每个 Entrance 都有自己的 Count 实例。

  • 主 資深大佬 : amiwrong123

    @hfc
    不是吧,我感觉只有一个 Count 对象存在吧。作为一个静态域

  • 資深大佬 : hfc

    @amiwrong123 哦,失误了,Count 这个我少看了一个 static 。。。

  • 主 資深大佬 : amiwrong123

    @hfc
    哎,所以现在我就有点没看懂这个打印结果了。。

  • 資深大佬 : etoah

    这个问题最主要的就 <print(this + ” Total: ” + count.increment());> 这句话

    前面的同步块 其实没什么用. 现在假设 5 个线程都走完同步块, 同时到达 <print(this + ” Total: ” + count.increment());>

    先将这个语句分成两个
    int countValue = count.increment();
    print(this + “Total:” + countValue);

    因为同时有 5 个线程在跑 我们将上面的代码复制 5 份 (第一行标识代码行号 十位数代表线程 id)

    pool_0 线程:
    01 int countValue00 = count.increment();
    02 print(this + “Total:” + countValue00);

    pool_1 线程:
    11 int countValue01 = count.increment();
    12 print(this + “Total:” + countValue01);

    pool_2 线程:
    21 int countValue02 = count.increment();
    22 print(this + “Total:” + countValue02);

    pool_3 线程:
    31 int countValue03 = count.increment();
    32 print(this + “Total:” + countValue03);

    pool_4 线程:
    41 int countValue04 = count.increment();
    42 print(this + “Total:” + countValue04);

    分别代表每一条线程执行的代码

    从输出结果看 cpu 执行的顺序是
    01 21 41 31 11
    02 32 12 22 42

    第一行执行后 countValue00 – 05 的值 分别是
    线程 pool_id: 0 2 4 3 1 // 取 cpu 执行顺序的十位数
    countValue : 1 2 3 4 5 // 按执行顺序递增

    第二行的顺序
    线程 pool_id: 0 3 1 2 4
    输出 countValue: 1 4 5 2 3

    是不是就对上了

    主要是 <print(this + ” Total: ” + count.increment());> 这条语句其实是不是一个原子操作

  • 主 資深大佬 : amiwrong123

    @etoah
    谢谢回答,你这么一理,确实是对上了。
    把那个语句拆分成两句确实清楚了,还有就是拆分后的第二句的执行顺序,跟第一句的不一样的原因:是不是因为,线程之间没有互斥锁,所以执行顺序就是随机的(跟线程池有关吗)?

    当然,每次打印结果都可能不一样,但至少,能用你的方法对上。

  • 資深大佬 : etoah

    @amiwrong123
    第二句和第一句执行顺序不同就是线程调度问题了
    每次结果不一样就是执行顺序随机的问题

    如果想控制线程执行顺序可以用线程同步的方法

  • 主 資深大佬 : amiwrong123

    @etoah
    谢谢,大概明白了。比如,我可以 Entrance 里改成 synchronized(Entrance.getClass()) {//run 里面的所有逻辑},这样就可以了。

    虽说这样改变程序的本意,Entrance 代表各个大门,那自然各个大门可以同时进入人,但这样改了,即使有五个大门,同一时刻,只能有一个大门可以进人。

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具