
synchronized基本使用方式synchronized主要有三种使用方式修饰实例方法修饰静态方法修饰代码块核心原则synchronized锁的不是代码本身而是某个对象对应的 Monitor 监视器锁。只有多个线程竞争同一个锁对象时才会产生互斥效果。1. 修饰实例方法publicclassCounter{privateintcount0;publicsynchronizedvoidincrement(){count;}}等价于publicclassCounter{privateintcount0;publicvoidincrement(){synchronized(this){count;}}}锁的对象实例同步方法锁的是this也就是当前对象实例。例如CountercounternewCounter();Threadt1newThread(counter::increment);Threadt2newThread(counter::increment);两个线程调用的是同一个counter对象因此竞争的是同一个counter实例锁不能同时进入increment()。但是不同实例之间不会互斥Countercounter1newCounter();Countercounter2newCounter();Threadt1newThread(counter1::increment);Threadt2newThread(counter2::increment);此时counter1!counter2两个线程使用的是不同实例对应的锁因此可以同时执行。结论synchronized修饰实例方法时锁的是当前对象this。同一个实例上的同步方法互斥不同实例上的同步方法不互斥。2. 修饰静态方法publicclassCounter{privatestaticintcount0;publicstaticsynchronizedvoidincrement(){count;}}等价于publicclassCounter{privatestaticintcount0;publicstaticvoidincrement(){synchronized(Counter.class){count;}}}锁的对象静态同步方法锁的是Counter.class也就是当前类对应的Class对象。无论创建多少个Counter实例静态同步方法竞争的都是同一把类锁Countercounter1newCounter();Countercounter2newCounter();Threadt1newThread(Counter::increment);Threadt2newThread(Counter::increment);两个线程都需要先获得Counter.class对应的锁因此不能同时执行静态同步方法。结论synchronized修饰静态方法时锁的是当前类的Class对象。该类的所有实例共享同一把类锁。3. 修饰代码块publicclassCounter{privateintcount0;privatefinalObjectlocknewObject();publicvoidincrement(){synchronized(lock){count;}}}锁的对象这里锁的是lock对象对应的 Monitor 监视器锁。执行过程是线程尝试获得 lock 的监视器锁 ↓ 获得成功进入同步代码块 ↓ 执行 count ↓ 退出同步代码块 ↓ 释放 lock 的监视器锁这里并不是直接把count锁住。count是被同步代码块保护的共享数据而lock是线程进入这段代码前必须获取的锁对象。可以理解为lock是钥匙count是需要钥匙才能进入执行的临界区。例如CountercounternewCounter();Threadt1newThread(counter::increment);Threadt2newThread(counter::increment);两个线程使用的是同一个counter也就使用同一个counter.lock因此两个线程不能同时执行count。4. 为什么锁对象使用final推荐写法privatefinalObjectlocknewObject();final可以保证lock引用始终指向同一个对象不能在运行过程中更换锁对象locknewObject();// 编译错误如果锁对象可以被更换privateObjectlocknewObject();publicvoidchangeLock(){locknewObject();}不同线程可能分别获得新旧两个锁对象从而无法形成互斥。因此自定义锁对象一般声明为privatefinalObjectlocknewObject();5. 同步代码块的优势同步代码块可以缩小加锁范围只保护真正需要同步的共享数据。例如publicvoiddoTask(){prepareData();synchronized(lock){count;}saveData();}其中prepareData();saveData();不访问需要保护的共享数据因此不需要放在锁中。这样可以减少线程持有锁的时间提高并发性能。相比之下如果直接锁整个方法publicsynchronizedvoiddoTask(){prepareData();count;saveData();}整个方法执行期间都会持有this锁其他线程等待时间更长。6. 只有使用同一把锁才有效正确写法privatefinalObjectlocknewObject();publicvoidincrement(){synchronized(lock){count;}}publicintgetCount(){synchronized(lock){returncount;}}读取和修改都使用同一个lock能够建立互斥和可见性保证。错误写法privatefinalObjectwriteLocknewObject();privatefinalObjectreadLocknewObject();publicvoidincrement(){synchronized(writeLock){count;}}publicintgetCount(){synchronized(readLock){returncount;}}读写使用不同的锁对象因此不能相互约束。另一个错误示例publicvoidincrement(){synchronized(newObject()){count;}}每次调用都会创建一个新的对象newObject()不同线程获得的是不同锁对象因此同步基本失效。7. 三种方式对比使用方式锁对象作用范围实例同步方法当前实例this整个实例方法静态同步方法当前类的Class对象整个静态方法同步代码块括号中指定的对象指定代码块代码速记publicclassCounter{privateintcount0;privatestaticintstaticCount0;privatefinalObjectlocknewObject();// 1. 实例同步方法锁 thispublicsynchronizedvoidincrement1(){count;}// 2. 静态同步方法锁 Counter.classpublicstaticsynchronizedvoidincrement2(){staticCount;}// 3. 同步代码块锁 lockpublicvoidincrement3(){synchronized(lock){count;}}}8. 速记synchronized有三种基本使用方式。修饰实例方法时锁的是当前对象this修饰静态方法时锁的是当前类对应的Class对象修饰代码块时锁的是括号中指定的对象。synchronized实际锁的是对象对应的 Monitor而不是直接锁住某个变量或某段代码。只有多个线程使用同一个锁对象时才会产生互斥效果。实际开发中通常使用private final Object lock作为内部锁并尽量缩小同步代码块的范围。最终速记// 实例方法锁 thispublicsynchronizedvoidmethod1(){}// 静态方法锁 当前类.classpublicstaticsynchronizedvoidmethod2(){}// 代码块锁指定对象privatefinalObjectlocknewObject();publicvoidmethod3(){synchronized(lock){}}实例方法锁this静态方法锁Class代码块锁指定对象锁对象相同才会互斥。