Java中的线程同步问题

时间:2019-06-06 16:12:56

标签: java multithreading runnable executorservice

我很清楚这可能是重复的,但是考虑到我的问题在这里我无法解决,因此我遇到了很多答案。

我将可运行对象与多个线程共享的对象进行了同步,并显式同步了我在内部使用的方法,但是程序的结果始终为3000。

我尝试锁定Counter类,但不会改变任何事情。 谁能解释我为什么在这个特定示例中我的所有操作都不起作用?

    public static void zad3() {
        var counter = new Counter();

        var toRun = new Runnable() {
            @Override
            public void run() {
                synchronized (counter) {
                    for (var i = 0; i < 1000; i++) {
                        counter.add(1);
                    }
                }
            }
        };

        var t1 = new Thread(toRun);
        var t2 = new Thread(toRun);
        var t3 = new Thread(toRun);

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("counter = " + counter.getCount());
   }
public class Counter {
    protected long count_ = 0;

    public synchronized void add(long value) {
        count_ += value;
    }

    public long getCount() {
        return count_;
    }
}

修改: 如建议的那样,问题在于循环在每个线程中不断运行1000次。 我的解决方案:

        var toRun = new Runnable() {
            @Override
            public void run() {
                synchronized (counter) {
                    for (var i = counter.getCount(); i < 1000; i++) {
                        counter.add(1);
                    }
                }
            }
        };

2 个答案:

答案 0 :(得分:2)

好吧,您已经在“ counter”变量周围同步了完整的for循环,这意味着每个线程将在该块中运行一次。 3 X 1000 = 3000

此块将在每个线程中执行一次

 for (var i = 0; i < 1000; i++) {
                        counter.add(1);
 }

更新:根据您的评论判断,您想中断1000个示例代码可以是:

 t1.start();
 t2.start();
 t3.start();

while(counter.getValue()<1000) {
    Thread.sleep(20)
}

另一个建议:

public class Incremetor extends Runnable {
   Counter counter;

public Incremetor(Counter counter) {
    this.counter = counter;
}
public void run() {
   counter.increment();
}

}

ExecutorService executorService = Executors.newFixedThreadPool(8); // this mean 8 threads in total to do your runnables.
for (int i=0;i<1000;++i) {
     executorService.submit(new Incrementor(counter));        
}

答案 1 :(得分:1)

因此,问题在于您让每个线程尝试1000次增量,因此您将需要以下类似内容:

while (counter.getCount() < 1000) {
     counter.add(1);
}

您提供的解决方案可能会为您提供正确的结果,但实际上您只是从1个线程开始递增计数器。当您使用synchronized(object) { }制作一个同步块时,所有线程都将尝试获取该块的锁,但是只有一个会。这意味着在您的解决方案中,第一个获得锁定的线程将执行所有1000个增量。当线程释放锁并让其他人获得它时,该工作已经完成。因此,实际上在3个线程之间分配增量的解决方案不应同步整个for循环。

如果运行我建议的while循环,您会更接近1000,但实际上可能会超过1000。请记住要运行程序10次或设置一个测试函数将其运行100次然后报告。问题是,从读取counter.getCount()的角度来看,该值可能已被另一个线程更改。要始终可靠地 获得1000,您可以确保对计数器的读写专有权:

while (true) {
    synchronized (counter) {
        if (counter.getCount() < 1000) {
            counter.add(1);
        } else {
            break;
        }
    }
}

请注意,像这样增加一个 变量的速度很慢。您只做1000次,但尝试使用10亿次。实际上,三线程版本需要1m17s(在我的PC上),而简单的顺序循环则需要约1.2秒。您可以通过在线程之间分配工作负载并让它们在具有专有权限的本地计数器上工作,然后最终添加结果来解决此问题。