JAVA synchronized代码块锁失效分析

249 阅读2分钟

synchronized 是我们的同步的一种选择,在我们的印象中被synchronized锁住的代码块,线程之间是互斥的。 其实不然! synchronized锁住的是一个对象,如果锁住的这个对象,在多个线程中相同,那么这些线程访问synchronized修饰的代码块时,总是互斥的。 但是,如果锁住的这个对象,在多个线程中是不同的,那么这些线程访问synchronized修饰的代码块,是不会互斥的! 下面演示一下错误加锁的的情况:

class Lock {
    public Lock(String id) {
        this.id = id;
    }

    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

错误加锁,不互斥

public class MySynchronzedTest {

    public static void main(String[] args) throws InterruptedException {
        Lock lock = new Lock("a");
        Lock lock1 = new Lock("a");
        new Thread(() ->{
            try {
                m1(lock);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        m1(lock1);
        Thread.sleep(10000);
    }

    /**
     * 加锁
     */
    public static void m1(Lock t) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "在排队" + new Date());
        synchronized (t) {
            System.out.println(Thread.currentThread().getName() + "获取到锁" + new Date());
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "结束了" + new Date());
        }

    }
}
main在排队Wed Aug 09 14:29:05 CST 2023
Thread-0在排队Wed Aug 09 14:29:05 CST 2023
main获取到锁Wed Aug 09 14:29:05 CST 2023
Thread-0获取到锁Wed Aug 09 14:29:05 CST 2023
Thread-0结束了Wed Aug 09 14:29:07 CST 2023
main结束了Wed Aug 09 14:29:07 CST 2023

可以看到在main线程未释放锁的时候,Thread-0也进来了,虽然lock和lock1的属性值相同,但他们都是new出来的的所以是不同的对象。

可以将锁对象换成Lock.class,在多个线程中相同,这样就可以实现互斥

public class MySynchronzedTest {

    public static void main(String[] args) throws InterruptedException {
        Lock lock = new Lock("a");
        Lock lock1 = new Lock("a");
        new Thread(() ->{
            try {
                m1(lock.getClass());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        m1(lock1.getClass());
        Thread.sleep(10000);
    }

    /**
     * 加锁
     */
    public static void m1(Class clazz) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "在排队" + new Date());
        synchronized (clazz) {
            System.out.println(Thread.currentThread().getName() + "获取到锁" + new Date());
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "结束了" + new Date());
        }

    }
}
Thread-0在排队Wed Aug 09 14:40:00 CST 2023
main在排队Wed Aug 09 14:40:00 CST 2023
Thread-0获取到锁Wed Aug 09 14:40:00 CST 2023
Thread-0结束了Wed Aug 09 14:40:02 CST 2023
main获取到锁Wed Aug 09 14:40:02 CST 2023
main结束了Wed Aug 09 14:40:04 CST 2023