synchronized关键字
synchronized 有两种加锁方式
(1) 某个对象实例内,防止多个线程对同一个实例对象的同时访问对该对象起作用
//synchronized方法
public synchronized void f() {
//TODO
}
//synchronized代码块
public void g() {
synchronized(this){
//TODO
}
}
synchronized方法所加的锁是通过ACC_SYNCHRONIZED 标识说明这是一个需要同步的方法。(此可通过Javap -c 命令反编译查看,下同)
synchronized所控制的代码块是通过指令monitorentry和monitorexit来获取锁和释放锁的。
每一个实例对象都是由对象头,实例数据,对齐填充组成
对象头由两部分组成:mark word以及类型指针(对象指向类元数据的指针,描述该实例是哪个类的实例)。其中Mark Word 中有一部分用来存储指向该实例的重量级锁的指针,并且有一个标志位来标识该实例是否被加锁。重量级锁是一个monitorObject对象,其中有等待队列(等待该锁的阻塞状态的线程),同步队列(某一线程释放该实例的锁之后等待队列里的阻塞线程移到此处),锁计数器(目前被重入次数+1,初值为0,代表还没有被线程所获取),目前锁的状态(当前哪个线程拥有该锁)。
当多个线程同时访问一段同步代码时,会先进入同步队列。jvm选择一个线程获取锁并运行,其他线程再次回到等待队列。
(2)某个类,防止多个线程同时访问同步代码块,可以对所有实例对象起作用
//synchronized类方法
public static synchronized void f(){
//TODO
}
public void g(){
synchronized(ClassName.class){
//TODO
}
采用synchronized锁机制的线程在等待时不能被interrupt()打断
public class SynchronizedBlocked implements Runnable{
public synchronized void f() {
System.out.println("Trying to call f()");
while(true) // Never releases lock
Thread.yield();
}
/**
* 在构造器中创建新线程并启动获取对象锁
*/
public SynchronizedBlocked() {
//该线程已持有当前实例锁
new Thread() {
public void run() {
f(); // Lock acquired by this thread
}
}.start();
}
public void run() {
//中断判断
while (true) {
if (Thread.interrupted()) {
System.out.println("中断线程!!");
break;
} else {
System.out.println("-----");
f();
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedBlocked sync = new SynchronizedBlocked();
Thread t = new Thread(sync);
//启动后调用f()方法,无法获取当前实例锁处于等待状态
t.start();
TimeUnit.SECONDS.sleep(1);
//中断线程,无法生效
t.interrupt();
}
}
运行结果:
-----
Trying to call f()
上述代码分析:在SynchronizedBlocked类的构造器中,有一个线程匿名内部类。每当new一个SynchronizedBlocked类的实例时都会开启一个线程。该线程在调用方法f()。一直循环调用Thread.yield()方法,方法yield()会使当前线程回到线程池,jvm重新从线程池中选择要运行的线程(有可能还会运行该线程)。yield()方法并不会释放该线程所拥有的锁,
以至于即使匿名线程给线程t让不了,但线程t永远都获取不到当前实例的锁,所以线程t一直处于阻塞状态。主线程在结束前调用了线程t的interrupt()方法,但线程t并没有被中断,可见interrupt()方法并不能中断被synchronized锁机制所阻塞的线程
。
synchronized关键字提供一种排它式同步机制,但有两个缺陷
(1)不能控制阻塞时长
线程一旦进入synchronized锁机制的阻塞状态,会一直等待,直到获取它所想要的锁。而且在阻塞的过程中,你并不知道要等待多久。
(2)阻塞不能被打断
线程在synchronized锁机制阻塞状态下,interrupt()方法并不能中断该线程。