java线程的结果不一致

kos*_*osa 6 java multithreading

我有一个实现runnable的线程类和一个int计数器作为实例变量.两个同步方法添加和子.当我以某种方式运行我的测试类时,它会在几次打印错误的结果.据我所知,当一个方法同步时,整个对象将被锁定以供其他线程访问,每当我们得到相同的结果时,这个逻辑是什么?有些情况并非如此.我错过了什么吗?

我的机器是Windows 7,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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

识别TestClass

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果

ADD counter1
ADD counter2
SUB counter1
SUB counter0
ADD counter1
SUB counter0
Run Code Online (Sandbox Code Playgroud)

注意:您可能需要执行几次运行才能产生这种不一致.

Ben*_*n S 5

您的结果看起来正确.

在执行方法期间,获得对象的排他锁,但是在add()sub()调用之间,线程可以自由交错.

如果0在所有线程都运行之后总共得到了总数,那么它们都没有覆盖eathother并且访问counter被同步.

如果你希望counter只是顺序而不是命中,那么执行以下操作(只要不涉及其他类,这将使方法级同步0变为冗余):12

@Override
public void run() {
    synchronize(this) {
        add();
        sub();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这使得线程的重点无用,因为您可以在单线程循环中执行此操作.

  • @JB Nizet:根据他发布的代码,不可能从2变为0.我怀疑他的输出是来自'sub`未同步的执行.这个猜想有一些证据:他发布的原始代码将`sub`方法的`synchronized`关键字放在代码无法编译的位置,这意味着在粘贴了一个版本后,他将synchronized关键字输入到他的注释中实际编译的代码.他原来的帖子记录在这里:http://stackoverflow.com/posts/8811535/revisions (3认同)

Edw*_*son 3

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

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

旁白:在某些情况下,在 上同步可能是不好的形式this,因为它是公共的 - 通常首选使用内部私有Object来锁定,这样其他调用者就无法获取您的锁并违反您设计的任何锁定策略。