我很清楚这可能被认为是重复的,但是考虑到我在这里的问题,我浏览了许多答案,我无法提出解决方案。
我将我的可运行对象与多个线程共享的对象同步,并显式同步了我在里面使用的方法,但程序的结果始终是 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);
}
}
}
};
好吧,您已经围绕"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));
}
所以问题是你让每个线程尝试 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亿。事实上,3 线程版本需要(在我的 PC 上)1 分 17 秒,而简单的顺序循环需要 ~1.2 秒。您可以通过在线程之间拆分工作负载并让它们在具有独占权限的本地计数器上工作,然后最终添加结果来解决此问题。