synchronized是java提供的原⼦性内置锁,这种内置的并且使⽤者看不到的锁也被称为监视器锁,使⽤synchronized之后,会在编译之后在同步的代码块前后加上monitorenter和monitorexit字节码指令,它依赖操作系统底层互斥锁实现。它的作⽤主要就是实现原⼦性操作和解决共享变量的内存可⻅性问题。
执⾏monitorenter指令时会尝试获取对象锁,如果对象没有被锁定或者已经获得了锁,锁的计数器+1。此时其他竞争锁的线程则会进⼊等待队列中。执⾏monitorexit指令时则会把计数器-1,当计数器值为0时,则锁释放,处于等待队列中的线程再继续竞争锁。
synchronized是排它锁,当⼀个线程获得锁之后,其他线程必须等待该线程释放锁后才能获得锁,⽽且由于Java中的线程和操作系统原⽣线程是⼀⼀对应的,线程被阻塞或者唤醒时时会从⽤户态切换到内核态,这种转换⾮常消耗性能。
从内存语义来说,加锁的过程会清除⼯作内存中的共享变量,再从主内存读取,⽽释放锁的过程则是将⼯作内存中的共享变量写回主内存。实际上⼤部分时候我认为说到monitorenter就⾏了,但是为了更清楚的描述,还是再具体⼀点。
如果再深⼊到源码来说,synchronized实际上有两个队列waitSet和entryList。
-
当多个线程进⼊同步代码块时,⾸先进⼊entryList
-
有⼀个线程获取到monitor锁后,就赋值给当前线程,并且计数器+1
-
如果线程调⽤wait⽅法,将释放锁,当前线程置为null,计数器-1,同时进⼊waitSet等待被唤醒,调⽤notify或者notifyAll之后⼜会进⼊entryList竞争锁
-
如果线程执⾏完毕,同样释放锁,计数器-1,当前线程置为null