Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
与java线程的结果不一致_Java_Multithreading - Fatal编程技术网

与java线程的结果不一致

与java线程的结果不一致,java,multithreading,Java,Multithreading,我有一个线程类,它实现了runnable和一个int计数器作为实例变量。两个同步的方法add和sub。当我运行我的测试类时,不知何故,它会几次打印一次错误的结果。据我所知,当一个方法被同步时,整个对象将被锁定以供其他线程访问,使用这种逻辑每次我们都应该得到相同的结果,对吗?有些人认为情况并非如此。我错过什么了吗 我的机器是Windows7,64位 public class ThreadClass implements Runnable { int counter = 0;

我有一个线程类,它实现了runnable和一个int计数器作为实例变量。两个同步的方法add和sub。当我运行我的测试类时,不知何故,它会几次打印一次错误的结果。据我所知,当一个方法被同步时,整个对象将被锁定以供其他线程访问,使用这种逻辑每次我们都应该得到相同的结果,对吗?有些人认为情况并非如此。我错过什么了吗

我的机器是Windows7,64位

 public class ThreadClass implements Runnable {

        int counter = 0;

        @Override
        public void run() {
            add();
            sub();
        }

        public synchronized void add() {
            System.out.println("ADD counter" + (counter = counter + 1));
        }

        public synchronized void sub() {
            System.out.println("SUB counter" + (counter = counter - 1));
        }
    }
测试类

public class ThreadTest {

    public static void main(String args[]) {
        ThreadClass tc = new ThreadClass();
        Thread tc0 = new Thread(tc);
        tc0.start();
        tc0.setPriority(Thread.MAX_PRIORITY);
        Thread tc1 = new Thread(tc);
        tc1.start();
        tc1.setPriority(Thread.NORM_PRIORITY);
        Thread tc2 = new Thread(tc);
        tc2.start();
        tc2.setPriority(Thread.MIN_PRIORITY);
    }
}
结果

ADD counter1
ADD counter2
SUB counter1
SUB counter0
ADD counter1
SUB counter0

注意:您可能需要运行几次以产生这种不一致性。

您的结果看起来是正确的

在方法执行期间,对象上会获得独占锁,但是在
add()
sub()
调用之间,线程可以自由交错

如果在所有线程都运行之后,您总共得到了
0
,那么它们都不会过度编写,并且对
计数器的访问也不会同步

如果您希望计数器只按顺序从
0
转到
1
,而不点击
2
,则执行以下操作(只要不涉及其他类,这将使方法级同步冗余):


但是,这使得线程的整个点毫无用处,因为您可以在单线程循环中执行该操作。

这两组结果都没有问题。它们都与代码的功能完全一致。无法保证多线程的运行顺序


您的“同步”方法确保获得有效的结果——对
add
的每次调用实际上都会添加一个,而对
sub
的每次调用实际上都会减去一个。如果没有它们,您可能会得到除零之外的最终结果。

同步确实意味着所有线程在进入同步块之前都将阻塞等待获取锁。只有一个线程可以锁定对象,因此只有一个线程可以在
add()
sub()
方法中

但是,这并不意味着关于线程顺序的任何其他内容。您将启动三个线程-唯一的保证是它们不会通过同时运行
add
sub
方法来互相践踏。线程1可以调用
add()
,然后线程3可以调用
add()
,然后线程2可以调用
add()
,然后它们都可以调用
sub()
。或者他们都可以调用
add()
,然后分别调用
sub()
。或者任何混合-唯一的要求是每个线程在调用
sub()
之前调用
add()
,并且当另一个线程在该方法中时,没有两个线程会调用
add()
sub()


旁白:在某些情况下,在
上同步此
,可能是一种不好的方式,因为它是公共的-通常首选使用内部私有
对象
来锁定,这样其他调用方就不会占用您的锁并违反您设计的任何锁定策略。

我同意以总计0语句结束,但如果我正确理解了同步,我不应该得到2,因为add/sub在同一个线程中,该线程最初锁定了对象进行添加。如果没有,同步关键字的真正用途是什么?你知道我在努力理解什么吗?当
run()
方法在其中一个线程上执行时,它不拥有锁(
run()
未同步)。当遇到
add()
调用时,它会尝试获取锁。如果锁不可用,它将等待。一旦
add()
调用完成,就会释放锁。此时,另一个线程可能正在等待它并在另一个线程的
sub()
有机会执行之前获取锁。这就是为什么两个
add()
调用可以连续发生的原因。虽然我同意您的解释,但它没有解释计数器如何从2变为0而不变为1。为什么System.out.println会对这些行重新排序?@JB Nizet:在他发布的代码下,从2到0是不可能的。我怀疑他的输出来自一次执行,
sub
未同步。这一推测有一些证据:他发布的原始代码将
sub
方法的
synchronized
关键字放置在代码无法编译的位置,这意味着他在粘贴了实际编译的代码版本后,在注释中键入了synchronized关键字。他的原始帖子记录如下:@DavidSchwartz对不起,我不清楚。我的观点是,由于发布的代码(两个方法都是同步的),日志输出永远不会显示从2到0的直接转换。同步两种方法时,必须在2和0之间打印中间值1。他的原始帖子包含的输出直接从2跳到0,没有显示中间值1。这是不可能的代码张贴。因此,他最初发布的程序不可能生成他最初发布的输出。此后,他编辑了输出,这使注释链变得混乱。您应该将
add
方法中的打印行更改为
add counter
,以便更容易看到正在发生的事情,还可以提供每个线程的名称和id,以便您可以看到哪个输出来自哪个线程。@ChrisWue。我使用更新的System.out再次运行。谢谢你的指点。我也试过使用私有对象锁(我发布的版本是两个相同结果的最后一个版本。现在我真正的问题是,任何带有实例变量的程序都可以是线程安全的吗?我的假设是因为设置了优先级,它应该按顺序执行。如果我需要确保按相同顺序触发的线程按相同顺序执行,我该如何实现它?@ThinkStiple:Why ar如果每个线程都必须等待另一个线程完成后才能开始,则使用线程
@Override
public void run() {
    synchronize(this) {
        add();
        sub();
    }
}