多线程环境中的增量和减量

时间:2018-06-11 11:08:28

标签: java multithreading concurrency

我在多线程环境中尝试经典的int变量递增/递减。这是我的示例代码。

public class SyncIncDec {


    public static void main(String[] args) {

        SyncCounter count = new SyncCounter();

        Thread incThread = new Thread(() -> {
            count.increment();
        });

        Thread decThread = new Thread(() -> {
            count.decrement();
        });

        Thread displayThread = new Thread(() -> {
            System.out.println("Count value : " + count.getX());
        });

        incThread.start();
        decThread.start();
        displayThread.start();      

        try {
            incThread.join();
        } catch (InterruptedException e) {
//          e.printStackTrace();
        }

        try {
            decThread.join();
        } catch (InterruptedException e) {
//          e.printStackTrace();
        }

        try {
            displayThread.join();
        } catch (InterruptedException e) {
//          e.printStackTrace();
        }

    }

}


class SyncCounter {

    private int x=0;

    public SyncCounter() {
        super();
    }

    public SyncCounter(int y) {
        super();
        x = y ;
    }

    synchronized int  getX() {
        return x; 
    }

    void setX(int y) {
        x = y ;
    }

    void increment() {
        ++x;
    }


    void decrement() {
        --x;
    }

}

虽然我已经为所有三个线程使用了join()方法,但我仍然得到不一致的结果。 这里没有加入意味着主线程要等到每个线程完成执行吗?我甚至尝试将synchronized同步添加到三个方法签名中的每一个;但我得到的结果不一致。

除了使用变量的原子版外,我还能怎样确保我总是得到0?

3 个答案:

答案 0 :(得分:4)

仅在启动所有线程后才在三个线程上调用join()。因此,您不能保证displayThread变量引用的线程在递增和递减计数器的线程之后运行。 为确保在启动它们之后在这些线程上调用join()

incThread.start();
decThread.start();
incThread.join();
decThread.join();
displayThread.start(); 

它将阻塞当前线程,直到执行递增和递减,并且在join()调用这些线程之后调用start()的任何顺序。

答案 1 :(得分:2)

您的SyncCounter根本不是线程安全的。应该同步可变方法的递增和递减。现在几天实现这样一个类的正确方法将在atomic orations。 例如:

class SyncCounter {

    private final AtomicInteger x;

    public SyncCounter() {
     this(0);   
    }

    public SyncCounter(int x) {
       this.x = new AtomicInteger(x);
    }

    int getX() {
        return x.get(); 
    }

    void setX(int x) {
        this.x.set(x);
    }

    int increment() {
        return x.incrementAndGet();
    }


    int decrement() {
        return x.decrementAndGet();
    }

}

测试代码:

    final Thread incThread = new Thread(() -> {
        count.increment();
    });

    final Thread decThread = new Thread(() -> {
        count.decrement();
    });

    Thread displayThread = new Thread(() -> {
        incThread.join();
        decThread.join();
        System.out.println("Count value : " + count.getX());
    });

答案 2 :(得分:0)

  

尽管我对所有三个线程都使用了join()方法,但结果仍然不一致。不是在这里加入就意味着主线程要等到每个线程都完成其执行之后?

您的代码中存在2个问题。

  • 在您的SyncCounter类中,仅getX()方法被同步。因为您有3个线程共享该类的相同实例,所以读取或更新共享字段的任何方法都必须为synchronized。这意味着increment()decrement()方法也必须是synchronized。如@Victor所述,用SyncCounter代替AtomicInteger是一个简单的解决方案,尽管我怀疑您的锻炼需要手工完成。

    ...
    synchronized int increment() {
    ...
    synchronized int decrement() {
    
  • 在线程模型中,递增和递减线程与显示线程之间存在竞争条件。仅仅因为您在其他线程之后启动显示线程并不意味着它最后运行。显示线程可能首先完成,在这种情况下,根据比赛条件,它将显示0或显示-1或1。此处最简单的解决方案是让主线程与增量线程和减量线程连接,然后 then 打印输出结果。

    // start the inc and dec threads running in the background
    incThread.start();
    decThread.start();
    // wait for inc thread to finish
    incThread.join();
    // wait for dec thread to finish
    decThread.join();
    // now we can print out the value of the counter
    System.out.println("Count value : " + count.getX());
    

    如果必须具有显示线程,则应在之后启动 像@davidxxx这样的增量和减量线程的联接 推荐。

    // start the inc and dec threads running in the background
    incThread.start();
    decThread.start();
    // wait for inc thread to finish
    incThread.join();
    // wait for dec thread to finish
    decThread.join();
    // now start the display thread now that the increment/decrement is done
    displayThread.start();
    // wait for the display thread to finish
    displayThread.join();