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

然后,如果您需要同步该方法,请确保它对于所有线程都是相同的,例如如果teststatic 对所有人都是一样的

@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多线程---同步与锁

Java线程同步块行为 - 同步与同步()? [复制]

java多线程并发系列之 (synchronized)同步与加锁机制

Qt系列文章之三十一 (基于QThread互斥量的线程同步线程)

Qt系列文章之三十一 (基于QThread互斥量的线程同步线程)

软件构造 并发3(线程安全性)----锁定和同步