java线程同步和锁定没有效果?
Posted
技术标签:
【中文标题】java线程同步和锁定没有效果?【英文标题】:java thread synchronized & lock no effect? 【发布时间】:2021-11-13 20:59:49 【问题描述】:为什么我在同步或锁定方法中执行“i++”时会得到随机结果?
public class aaa implements Runnable
static int count = 0;
public static void main(String[] args)
aaa aaa = new aaa();
aaa.create();
public void create()
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 1000; i++)
aaa thread = new aaa();
executor.execute(thread);
executor.shutdown();
while (true)
if(executor.isTerminated())
System.out.println("a " + count);
break;
@Override
public void run()
this.test();
public void test()
Lock lock = new ReentrantLock();
try
lock.lock();
count++;
System.out.println(count);
finally
lock.unlock();
或者:
public synchronized void test()
count++;
System.out.println(count);
结果是一个随机数,有时是 1000,有时是 998、999 ...等等,而“测试”方法内部的打印不是按顺序排列的,就像:
867
836
825
824
821
820
819
817
816
a 999
但是,如果它在同步块中,一切看起来都不错:
public void test()
synchronized (aaa.class)
count++;
System.out.println(count);
结果:
993
994
995
996
997
998
999
1000
a 1000
我认为上面所有的方法都应该给我相同的结果1000,并且自增应该是顺序的,但只有最后一个方法有效。代码有什么问题?请帮忙!!!
【问题讨论】:
【参考方案1】:经验法则:在你想用它保护的变量之后的下一行声明你的锁变量,并用相同的关键字声明它。例如,
public class aaa implements Runnable
static int count = 0;
static Lock countLock = new ReentrantLock();
...
如果您对此处的任何其他答案进行了足够深入的阅读,那么您就会明白为什么这会有所帮助。
【讨论】:
【参考方案2】:您必须创建一个互斥体,即
static Lock lock = new ReentrantLock();
您的同步方法不起作用,因为您正在创建 N aaa
实例然后,每个(非静态)方法都是不同的(具有自己的互斥锁)。
您的synchronized (aaa.class)
有效,因为aaa.class
对于所有aaa
实例和方法都是相同的Object
。
然后,如果您需要同步该方法,请确保它对于所有线程都是相同的,例如如果test
是static
对所有人都是一样的
@Override
public void run()
test();
public static synchronized void test()
count++;
但你可以注入一个“计数器类”,例如
class Counter
int count = 0;
// non static but synchronized for all (since they use the same `counter` object)
synchronized void inc()
count++;
用于所有线程
...
SyncTest thread = new SyncTest(counter); // <== the same
...
(完整代码)
public class SyncTest implements Runnable
private final Counter c;
public SyncTest(Counter c)
this.c = c;
static class Counter
int count = 0;
// non static but synchronized for all (since they use the same `counter` object)
synchronized void inc()
count++;
@Override
public void run()
test();
public void test()
this.c.inc();
public static void main(String[] args)
// one counter for all
Counter counter = new Counter();
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 10000; i++)
SyncTest thread = new SyncTest(counter);
executor.execute(thread);
executor.shutdown();
while (true)
if (executor.isTerminated())
System.out.println("a " + counter.count);
break;
【讨论】:
谢谢大佬,既然这种情况下同步方法不起作用,那么在什么情况下同步方法有效呢?可以举个例子吗? @ee 有很多方法可以做到这一点,我已经用两个更新了答案【参考方案3】:您正在创建 aaa 的多个实例,每个实例都创建自己的 ReentrantLock,并且每个执行中的线程都顺利地从自己的实例中获取锁。
public void test()
Lock lock = new ReentrantLock();
try
lock.lock();
count++;
System.out.println(count);
finally
lock.unlock();
由于 aaa 有多个实例,每个线程都在自己的实例上运行,同步方法使用 aaa.class 的当前对象
public synchronized void test()
count++;
System.out.println(count);
在这种方法中获得正确结果的原因是,您使用 aaa.class 作为同步对象
public void test()
synchronized (aaa.class)
count++;
System.out.println(count);
解决方案是,在所有线程中重用相同的锁(ReentrantLock)。将锁定义在与变量计数相同的级别将解决问题。
【讨论】:
以上是关于java线程同步和锁定没有效果?的主要内容,如果未能解决你的问题,请参考以下文章
java多线程并发系列之 (synchronized)同步与加锁机制
Qt系列文章之三十一 (基于QThread互斥量的线程同步线程)