加锁常见的问题

锁其是用来控制在某些场景下让代码串行的工具。我们为了充分利用计算机的硬件性能,发明了多线程,多线程有好处,但同时也有它复杂的一面,必须控制好多个线程的执行,才能驯服这个有能力也有脾气的烈马。

一、加锁范围误区

下面add()方法加了锁的,但是compare()方法中还是会出现a != b的场景。必须要考虑锁的范围。add()方法加了同步,只会保证多个线程在执行add()方法是串行的,也即保证a和b一起递增。但是compare()方法并不和add()方法串行,所以,当add()方法执行到一半时,是有可能执行compare()方法,也就是出现a大于b的情况。

@Slf4j
public class Test {

    public static void main(String[] args) {
        Test test = new Test();
        new Thread(()->test.add()).start();
        new Thread(()->test.compare()).start();
    }


    volatile int a = 1;
    volatile int b = 1;

    public synchronized void add() {
        log.info("add start");
        for (int i = 0; i < 10000; i++) {
            a++;
            b++;
        }
        log.info("add done");
    }

    public void compare() {
        log.info("compare start");
        for (int i = 0; i < 10000; i++) {
            if (a != b) {
                log.info("a:{},b:{},{}", a, b, a > b);
            }
        }
        log.info("compare done");
    }

}

 执行结果:

二、 锁级别的误判

要让多个线程串行执行,宗旨就是让他们去争同一个东西,我们姑且把这个东西称为锁。在JAVA中,锁可以为类级别和实例级别。

下面代码中,add()方法就是实例级别的锁,导致多线程下不同实例并不会串行,但是coun又是多线程共享的,导致,自增操作被破坏,无法达到预期的值。
 

@Slf4j
public class Test {
    private static int count;

    public static void main(String[] args) {
        IntStream.rangeClosed(1, 1000).parallel().forEach(i -> new Test().add());
        log.info("count={}", Test.count);
    }

    public synchronized void add() {
        count++;
    }
}

执行结果:

 

猜你喜欢

转载自blog.csdn.net/tales522/article/details/133616242