简体   繁体   English

Java:多个线程通过同步方法同时访问变量来减少静态变量

[英]Java: Multiple threads decrementing static variable via synchronized method accessing variable at the same time

I'm trying to synchronize my Person class Methods so that my static counter variable gets decremented by one thread at the a time. 我正在尝试同步我的Person类方法,以便我的静态计数器变量一次减少一个线程。

public class Person extends Thread {

    private static int count = 10;

    public void decrement() {
        synchronized(Person.class) {
            count--;
        }

    }

    public int getCount() {
        return count;
    }

    public void run(){
        while( count > 0){
            this.decrement();
            System.out.print(this.getCount() + ",");
        }
    }
}

Here's my main class. 这是我的主要课程。 Each thread will decrement to static counter via synchronized method to avoid mutiple thread access to same resource. 每个线程将通过synchronized方法递减到静态计数器,以避免多个线程访问相同的资源。

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        Person p1 = new Person();
        Person p2 = new Person();
        Person p3 = new Person();
        Person p4 = new Person();
        Person p5 = new Person();

        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
    }
}

But when i run my program, it is printing duplicate counter values. 但是当我运行我的程序时,它正在打印重复的计数器值。 What am i doing wrong? 我究竟做错了什么?

Outputs: 输出:

8,8,7,6,5,4,3,2,1,0

8,7,5,3,1,0,6,8,4,0

What's going on in the original code is the following: 原始代码中发生的事情如下:

  1. Thread one decrements count (count -> 9) 线程一减少计数(计数 - > 9)
  2. Thread two decrements count (count -> 8) 线程两个减量计数(计数 - > 8)
  3. Thread three decrements count (count -> 7) 线程三个减量计数(计数 - > 7)
  4. Thread four decrements count (count -> 6) 线程四个减量计数(计数 - > 6)
  5. Thread one outputs count (count: 6) 线程一输出计数(计数:6)
  6. Thread two outputs count (count: 6) 线程两输出计数(计数:6)
  7. Thread three outputs count (count: 6) 线程三输出计数(计数:6)
  8. Thread four outputs count (count: 6) 线程四输出计数(计数:6)

Since you are locking the decrement and not the decrement and output together, it's appearing to decrement multiple times. 由于您锁定了减量而不是减量和输出,因此它似乎会减少多次。

In other words, there is no guarantee that this code will execute back-to-back: 换句话说,无法保证此代码将背靠背执行:

        this.decrement();
        System.out.print(this.getCount() + ",");

Here's the fixed code. 这是固定代码。 It returns the current count value on decrement so that the new value can be returned and printed. 它会在递减时返回当前计数值,以便可以返回并打印新值。

public class Person extends Thread {

    private static int count = 10;

    public int decrement() {
        synchronized(Person.class) {
            count = count - 1;
            return count;
        }

    }

    public int getCount() {
        synchronized(Person.class) {
            return count;
        }
    }

    public void run(){
        while( getCount() > 0){
            int count = this.decrement();
            System.out.println(count);
        }
    }
}

I'd recommend AtomicInteger though for this task: 我建议使用AtomicInteger执行此任务:

import java.util.concurrent.atomic.AtomicInteger;

public class Person extends Thread {

    private static AtomicInteger count = new AtomicInteger(10);

    public int decrement() {
        return count.decrementAndGet();
    }

    public void run(){
        while(count.get() > 0){
            int currentCount = this.decrement();
            System.out.print(currentCount + ",");
        }
    }
}

It's not enough to synchronize only writes, you have to synchronize the getter as well (otherwise the reader thread may read a stale value). 仅仅同步写入是不够的,您还必须同步getter(否则读取器线程可能会读取过时的值)。 But in this case the problem is that other threads can interleave executing between the time a thread decrements and the time that same thread retrieves a value. 但在这种情况下,问题是其他线程可以在线程递减的时间和同一线程检索值的时间之间交错执行。

Use a java.util.concurrent.atomic.AtomicInteger to store the count. 使用java.util.concurrent.atomic.AtomicInteger存储计数。 But if you keep separate methods for the decrementing and the getting (locking the decrement and lock the getter separately), still there's nothing to guarantee that threads don't interleave in a way that causes duplicates to get written out. 但是如果你为减量和获取保持不同的方法(锁定减量并分别锁定getter),仍然没有什么可以保证线程不会以导致重复写出的方式进行交错。 Using AtomicInteger's decrementAndGet method makes sure that the value that's decremented is the one that's returned. 使用AtomicInteger的decrementAndGet方法可确保递减的值是返回的值。

Proper synchronization is the key, but using an AtomicInteger isn't the entire answer. 正确的同步是关键,但使用AtomicInteger并不是完整的答案。 What you need to realize is that each thread needs to report on the just-decremented count, which may be changed by another thread even if you're 1) using an AtomicInteger or 2) properly (separately) synchronizing both the decrement and getCount methods. 你需要意识到的是,每个线程需要报告刚递减的计数,即使你是1)使用AtomicInteger或2)正确(单独)同步decrementgetCount方法,也可以通过另一个线程进行更改。

The body of the while loop is a critical section , a section of code that must not be interrupted. while循环的主体是一个关键部分 ,一段不能被中断的代码。

public void run(){
    while( count > 0){
        synchronized (Person.class)
        {
            decrement();
            System.out.print(getCount() + ",");
        }
    }
}

Output: 输出:

9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4

Sometimes it stops at -3 . 有时它会在-3停止。 Now, each instance is free to keep going and decrement, because it checks the while condition which passes, then the thread is interrupted, then another thread decrements. 现在,每个实例都可以自由地继续进行和减少,因为它检查通过的while条件,然后线程被中断,然后另一个线程递减。 Then the original thread decrements even though it's already down to 0 ! 然后原始线程减少,即使它已经降到0 Check inside the loop. 检查循环内部。

public void run(){
    while( count > 0){
        synchronized (Person.class)
        {
            if (count > 0)
            {
                decrement();
                System.out.print(getCount() + ",");
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM