在java中,可以使用synchronized方法是不可靠的?(In java, can the u

2019-10-16 17:04发布

直行到这一点,我已经做出了代码在Java测试并发性,使用同步方法:

码:

public class ThreadTraining {

    public static class Value {

        private static int value;

        public static synchronized void Add() {
            value++;
        }

        public static synchronized void Sub() {
            value--;
        }

        public static synchronized int Get() {
            return value;
        }
    }

    public static class AddT implements Runnable {

        public static String name;

        public AddT(String n) {
            name = n;
        }

        @Override
        public void run() {
            while (Value.Get() < 100) {
                int prev = Value.Get();
                Value.Add();
                System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
            }
        }
    }

    public static class SubT implements Runnable {

        public static String name;

        public SubT(String n) {
            name = n;
        }

        @Override
        public void run() {
            while (Value.Get() > (-100)) {
                int prev = Value.Get();
                Value.Sub();
                System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
            }
        }
    }

    public static void main(String[] args) {
        Thread threads[] = new Thread[3];
        for (int i = 0; i < 4; i++) {
            if (i % 2 == 0) {
                threads[i] = new Thread(new AddT("Adder - Thread #" + i));
            } else {
                threads[i] = new Thread(new SubT("Subtractor - Thread #" + i));
            }
            threads[i].start();
        }
    }
}

此代码,坚韧,具有尽管不可靠的执行力“这是不可能的同一对象,以交错的同步方法两个调用。” (来源: Oracle的并发教程- synchronized方法 ),使输出是不可靠的,如以下内容:(注意,在输出中任何非patternful改变了“......”字里行间表示, 不仅是不可靠的行为

-----[Thread #0 - Adder] has been created!
=====[Thread #0 - Adder] has been started!
-----[Thread #1 - Subtractor] has been created!
[Thread #0 - Adder] - changed value from 0 to 1
[Thread #0 - Adder] - changed value from 1 to 2
[Thread #0 - Adder] - changed value from 2 to 3
...\*goes on, adding as expected, for some lines*\
[Thread #0 - Adder] - changed value from 83 to 84
[Thread #0 - Adder] - changed value from 84 to 85
-----[Thread #2 - Adder] has been created!
=====[Thread #1 - Subtractor] has been started!
[Thread #0 - Adder] - changed value from 85 to 86
[Thread #1 - Subtractor] - changed value from 86 to 85
[Thread #1 - Subtractor] - changed value from 86 to 85
[Thread #1 - Subtractor] - changed value from 85 to 84
...\*goes on, subtracting as expected, for some lines*\
[Thread #1 - Subtractor] - changed value from -98 to -99
[Thread #1 - Subtractor] - changed value from -99 to -100 \*This thread ends here, as it reaches the state where (value>(-100))==false*\
=====[Thread #2 - Adder] has been started!
[Thread #2 - Adder] - changed value from -100 to -99
[Thread #2 - Adder] - changed value from -99 to -98
[Thread #2 - Adder] - changed value from -98 to -97
...\*goes on as expected...*\
[Thread #2 - Adder] - changed value from -67 to -66
[Thread #2 - Adder] - changed value from -66 to -65
-----[Thread #3 - Subtractor] has been created!
=====[Thread #3 - Subtractor] has been started!
[Thread #3 - Subtractor] - changed value from -65 to -66
[Thread #3 - Subtractor] - changed value from -66 to -67
...\*goes on as expected...*\
[Thread #3 - Subtractor] - changed value from -71 to -72
[Thread #3 - Subtractor] - changed value from -72 to -73 \*NOTE: From -73 it goes to -74, without a Subtractor-action in between! WTF???*\
[Thread #2 - Adder] - changed value from -74 to -73
[Thread #2 - Adder] - changed value from -73 to -72
...\*goes on as expected...*\
[Thread #2 - Adder] - changed value from 98 to 99
[Thread #2 - Adder] - changed value from 99 to 100 \*This adder ends here, adder thread #0 probably ends after next line...but not before doing something crazy!*\
[Thread #0 - Adder] - changed value from 85 to 86 \*What the hell are these values doing here? Oh wait, next lines is...*\
[Thread #3 - Subtractor] - changed value from -73 to -47\*...Holy mother of god!*\
[Thread #3 - Subtractor] - changed value from 100 to 99
[Thread #3 - Subtractor] - changed value from 99 to 98
...
[Thread #3 - Subtractor] - changed value from -98 to -99
[Thread #3 - Subtractor] - changed value from -99 to -100 \*The logical nightmare is finally over.*\

是采用同步方法不可靠? 或者是执行错了吗? (如果是这样,有什么不好呢?以及如何解决的呢?)

Answer 1:

你的实现是有点过。 “获取”,“添加”和“子”均锁定,但你的“获取”和你的加法或减法之间的间隙。 只是没有“获取”线程可以采取的间隙期间休息,和别人改变值。 如果需要多个方法调用一切发生作为一个单一的操作,你需要的东西“大”比​​单个方法同步。

synchronized (Value.class) {
  int prev = Value.Get();
  Value.Add();
  System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
}

请注意,这还是有一个问题,当你进入<100可能不会仍然是真实的,所以你应该重新检查它。 (当然地锁定一个Class对象上的是不是你通常会想在“真实”的代码做一些事情:))



Answer 2:

要了解什么是你的代码错误,请考虑以下几点:

int prev = Value.Get();
Value.Sub();
System.out.println(... + Value.Get());

每三个synchronized操作Get() Sub() Get()保证通过正确并自动运行。 但是,不能保证其他线程不会这些操作之间进来和修改Value

每当你需要确保操作序列的原子,你要么提供一个单一的synchronized ,在一次执行所有这些方法,或者使用外部synchronized块,像这样:

synchronized (Value.class) {
    int prev = Value.Get();
    Value.Sub();
    System.out.println(... + Value.Get());
}


Answer 3:

什么是你看到的是不是 “不可靠”,也不是一个实现问题。 您正在处理大量的竞争条件。 例如:

while (Value.Get() < 100) {
     // other threads could have called `Add()` or `Subtract()` here
     int prev = Value.Get();
     // other threads could have called `Add()` or `Subtract()` here
     Value.Add();
     // other threads could have called `Add()` or `Subtract()` here
     System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
}

你打电话Get() 3次在这个循环,也不能保证其他线程已经改变每个调用之间的值,以Get() 如果你改变你的代码像下面,它会产生更加一致的输出:

while (true) { {
     // only call get once
     int prev = Value.Get();
     if (prev >= 100) {
        break;
     }
     // return the new value from add
     int newValue = Value.Add();
     System.out.println("[" + name + "] - changed value from " + prev + " to " + newValue);
}

// here's the new version of add
public static synchronized int Add() {
    value++;
    return value;
}

请注意,那里仍是之间的竞争条件Get()Add()方法调用,因为它是一个测试,然后一组。



Answer 4:

这里是一个完美运行的代码。 它补充由@Affe和@Aix这里给出的答案。

public class ThreadTraining {

    public static class Value {

        private static  int value;

        public static synchronized void Add() {
            value++;

        }

        public static synchronized void Sub() {
            value--;
        }

        public static synchronized int Get() {
            return value;
        }
    }

    public static class AddT implements Runnable {

        public static String name;

        public AddT(String n) {
            name = n;
        }

        @Override
        public void run() {

            while (Value.Get() < 100) {
                 synchronized(Value.class){
                int prev = Value.Get();
                Value.Add();
                System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
            }
            }
        }
    }

    public static class SubT implements Runnable {

        public static String name;

        public SubT(String n) {
            name = n;
        }

        @Override
        public void run() {

            while (Value.Get() > (-100)) {
            synchronized(Value.class){
                int prev = Value.Get();
                Value.Sub();
                System.out.println("[" + name + "] - changed value from " + prev + " to " + Value.Get());
            }
            }
        }
    }

    public static void main(String[] args) {
        Thread threads[] = new Thread[3];
        for (int i = 0; i < 4; i++) {
            if (i % 2 == 0) {
                threads[i] = new Thread(new AddT("Adder - Thread #" + i));
            } else {
                threads[i] = new Thread(new SubT("Subtractor - Thread #" + i));
            }
            threads[i].start();
        }
    }
}

通过不把同步块如图中修改后的代码,你让线程抓住而在操作中的变量值被原子地完成。 因此所得到的不一致性。



文章来源: In java, can the use of synchronized methods be unreliable?