Synchronized关键字参数的理解

508 阅读3分钟

Synchronized关键字Java提供的一种内置的锁机制来支持原子性和可见性。每一个Java对象都可以用做一个实现同步的锁,这种锁被称为内置锁(Intrinsic Lock)或者 监视器锁(Monitor Lock)。线程进入同步代码块之前会自动获得锁,并且在退出同步代码块时(正常返回,或者是异常退出)会自动释放锁。

  • synchronized是一种隐式互斥锁,一次只能允许一个线程进入同步代码块
  • Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的!
  • synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)
  • synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是立即可见的)

synchronized修饰方法时很容易理解,要么是静态方法,要么是非静态方法。 我们知道synchronized关键字修饰代码块时参数可以是:this,xxx.class,和任意的实例。这个几种情况有什么区别呢?

一个很简单的测试

public class ThreadTest implements Runnable{
    @Override
    public void run() {
        String lock = "lock";
        //String lock = new String("lock");
        System.out.println(lock.hashCode());
        synchronized (lock){
            while (true){
                System.out.println(Thread.currentThread().getName()+"-----------正在执行");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        ThreadTest t1 = new ThreadTest();
        ThreadTest t2 = new ThreadTest();
        new Thread(t1).start();
        new Thread(t2).start();

    }
}

起初我的代码是这样的,但是却只有一个线程能执行。但这是两个不同的Runnable实例,应该不会阻塞才对啊。然后我去搜了几篇文章,才发现别人的String是new出来的,这时我才想起来,不用new的话String字符串是存在常量池中的。实际上两个线程中的lock字符串是同一个。这就导致了这个类的所有实例都会阻塞,我们可以打印一下hash值来判断是不是同一个地址。

image.png

synchronized(获取锁的地方或者叫判断的地方){
        工作内容 
}

锁是很抽象的概念,我直接理解成一个字段,他可以来自任何java的实例,当一个线程来执行被synchronized修饰的代码时,都会判断一下这个字段(假设为一个boolean类型)我能否执行其中的代码?能就修改这个字段为false(true代表可以调用)表示已经这段代码已经锁住了,其他线程再来判断这个字段时为false不能执行,就会等待。synchronized关键字的参数就表示这个以哪个地方的这个字段为基准判断,可以为某个实例,也可以是某个类的class,或者常量池中的数据。如果是唯一的那么这就是个类锁,会阻塞调用同步方法的所有实例;如果不唯一,每个实例的都是不同的(物理上的指内存地址),那么就不同实例调用同步方法就不会阻塞。