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

时间:2016-04-20 19:10:23

标签: java multithreading static synchronization synchronized

我正在尝试同步我的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() + ",");
        }
    }
}

这是我的主要课程。每个线程将通过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();
    }
}

但是当我运行我的程序时,它正在打印重复的计数器值。我做错了什么?

输出:

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

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

3 个答案:

答案 0 :(得分:5)

原始代码中发生了以下情况:

  1. 线程1减少计数(计数 - > 9)
  2. 线程两个减量计数(count - > 8)
  3. 线程三个减量计数(count - > 7)
  4. 线程四个减量计数(count - > 6)
  5. 线程1输出计数(计数:6)
  6. 线程两个输出计数(计数:6)
  7. 线程三输出计数(计数:6)
  8. 线程四输出计数(计数:6)
  9. 由于你是锁定减量而不是减量和输出,所以它似乎会减少多次。

    换句话说,无法保证此代码将背靠背执行:

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

    这是固定代码。它会在减量时返回当前计数值,以便可以返回并打印新值。

    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);
            }
        }
    }
    

    我建议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 + ",");
            }
        }
    }
    

答案 1 :(得分:3)

仅仅同步写入是不够的,您还必须同步getter(否则读取器线程可能会读取过时的值)。但在这种情况下,问题是其他线程可以在线程递减的时间和同一线程检索值的时间之间交错执行。

使用java.util.concurrent.atomic.AtomicInteger存储计数。但是如果你为减量和获取保持不同的方法(锁定减量并分别锁定getter),仍然没有什么可以保证线程不会以导致重复写出的方式进行交错。使用AtomicInteger的decrementAndGet方法可确保递减的值是返回的值。

答案 2 :(得分:1)

正确的同步是关键,但使用AtomicInteger并不是整个答案。您需要意识到的是,每个线程都需要报告刚递减的计数,即使您1)使用AtomicInteger或2)正确(单独)同步,也可能会被另一个线程更改decrementgetCount方法。

while循环的主体是关键部分,是一段不得中断的代码。

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

输出:

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

有时会在-3停止。现在,每个实例都可以自由地继续和减少,因为它检查通过的while条件,然后线程被中断,然后另一个线程递减。然后原始线程减少,即使它已经下降到0!检查循环内部。

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