我开始学习一些java并发概念并投入使用。但这段代码中有一段超出了我的理解范围。

public class Count { 
    private int count = 0; 
    public synchronized void setCount(int count) { 
        this.count = count; 
    } 
    public synchronized int getCount() { 
        return count; 
    } 
} 
class CountRunner extends Thread { 
    Count count; 
    public CountRunner(Count count) { 
        this.count = count; 
    } 
    public void run() { 
        for (int i = 1; i <= 1000; i++) { 
            count.setCount(count.getCount() + 1); 
        } 
    } 
} 
class TestCount { 
    public static void main(String[] args) throws Exception { 
        Count count = new Count(); 
        CountRunner runnerA = new CountRunner(count); 
        CountRunner runnerB = new CountRunner(count); 
        runnerA.start(); 
        runnerB.start();         
        runnerA.join(); //join statement here 
        runnerB.join(); 
        System.out.println("count.getCount = " + count.getCount()); 
    } 
} 
Question:
1. The result is a little less than 2000 for many times, why ?
2. if delete 2 join() statement, why count.getCount = 451,even less ?
3. i think there will be no effect of deleting the join() statements,
because i already have Synchronized method to lock one object to one thread each time ? 
So, What's the point of using Synchronized and join() ?
有帮助吗?

解决方案

很简单。您调用setCount方法通过调用getCount + 1.在进入方法之前,运行时评估GetCount(同步),但在离开GetCount并输入SetCount时,您不会按住锁定,其他线程可以进入呼叫GetCount。所以每一个现在,然后是两个(或更多,具体取决于您创建的有多少线程)线程在getCount中具有相同的值。想象一下,线程进入并在getCount中接收值1。运行时会产生它对踩踏B调用GetCount并接收相同值1.线程B将值设置为1并再次运行,因此在该阶段将是50的值。运行时会产生对线程A的执行,其中包含1(请记住它不设)调用setCount并产生它的Exec)。现在,将值设置为1(错误)。

更改您运行的实现:

public void run() { 
    for (int i = 1; i <= 1000; i++) {
      synchronized(count){ 
        count.setCount(count.getCount() + 1); 
      }
    } 
} 
.

其他提示

  1. 如果打破行

    count.setcount(count.getcount()+ 1);

    分为3个单独的线条,它将更清晰:

    final int oldCount = count.getCount(); // a
    final int newCount = oldCount + 1; // b
    count.setCount(newCount); // c
    
    .

    请注意,虽然语句(a)和(c)各自同步,但整个块 not 。因此,它们仍然可以交错,这意味着线程a可以在线程b执行线程(a)之后输入/执行语句(a),但在它完成/输入语句之前 。当发生线程(a)和(b)时,它将具有相同的 oldcount 并因此错过了一个增量。

    2.

    join()是在打印之前确保两个线程A和线程B完成。如果您打印结果时,您获得较小的计数的原因是尚未完成尚未完成运行的。换句话说,即使您完全同步,无需加入(),您仍然有一个小于2000的数字。

    3。 请参阅回答2。

1)因为你没有正确锁定。你正在呼唤 getCount(), ,锁定、获取值并解锁、递增并调用 setCount() 它会锁定、保存值并解锁。在某些情况下,两个线程都会调用 getCount(), ,第一个踏板锁,获取值 x并解锁。然后第二个线程获得锁,获得相同的值 x并解锁。由于两个线程都会递增并随后保存相同的值,因此您将获得比预期更低的计数。

2) 没有 join() 你不会等待你的线程完成,你的主线程只会调用 getCount() 并在线程仍在运行时获取随机值。

3)由于其他线程没有持有锁 每时每刻 (如果你想让他们都运行,他们毕竟需要给彼此解锁时间),你需要 join()仍然。

someThread.join() 会导致调用 Thread 等到 someThread 已完成。

如果您删除 join() 调用,然后是主要的 Thread 可能会打电话 getCount() 在计数结束之前,因为 someThread 可能仍在运行。

同步方法仅仅意味着对一个对象的多个同步调用 Object 不能在 同时.

一行答案是 count.setCount(count.getCount() + 1); 不是原子操作。

或者稍微不那么神谕,同时 setCountgetCount 是正确的线程安全和原子的,没有什么可以阻止另一个线程调用这些方法中的任何一个 之间 该线程的调用 setCountgetCount. 。这将导致计数丢失。

避免丢失计数的一种方法是创建一个原子 increment() 手术。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top