synchronized 是Java 提供的内置锁机制,可以用在代码块上,也可以直接用在方法上。
这里有两个概念,锁对象,和代码块。
synchronized锁的是对象,任意一个Java 对象都可以作为锁对象,但是不能用String常量,Integer,Long等基础数据对应的封装类型,锁对象可以为Object,例如
Object obj = new Object();
synchronized(obj) {
}
如果不想每次都使用new对象的方法,也可以简化为当前对象,必须先要获取this的锁,才能执行后面的代码,如
synchronized(this) {
}
另外,静态方法没有this对象,则是以Class类对象作为锁。
static void method() {
synchronized(ClassA.class) {
}
}
另外一种使用方式,使用的锁为method方法调用所在的对象。
synchronized static void method() {
}
synchronized修饰的同步方法可以和非同步方法同时调用;它还有一个特点,可重入性,即如果一个线程已经有了这把锁,还可以再次申请获得这把锁,比如一个同步方法method1里调用另一个同步方法method2,两个方法给同一个对象加锁,此时,当某个线程执行method1的时候,是可以顺利执行method2的,不会出现死锁。
简单举个使用栗子:
public class SynchronizedTest {
private String key;
private String value;
public synchronized void set(String key, String value) {
System.out.println(Thread.currentThread().getName());
this.key = key;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.value = value;
}
public void get() {
System.out.println(Thread.currentThread().getName()+":"+this.key+":"+this.value);
}
public synchronized void getValue() {
System.out.println(Thread.currentThread().getName()+":"+this.key+":"+this.value);
}
public synchronized void setAndGetValue() {
getValue();
set("Cindy","CindyValue");
System.out.println(this.key+":"+this.value);
}
public static void main(String[] args) {
SynchronizedTest test = new SynchronizedTest();
new Thread(()->test.set("Alice", "value1"),"thread1").start();
new Thread(()->test.get(),"thread2").start();
new Thread(()->test.getValue(),"thread3").start();
new Thread(()->test.setAndGetValue(),"thread4").start();
}
}
synchronized 锁升级过程:
synchronized(obj) 方法,第一个线程访问的时候,在obj 头部 markword 记录这个线程的id,此时为偏向锁;
当偏向锁有线程争用的时候,会升级为自旋锁,使用 while 循环等待占用 cpu;
自旋锁循环10次后,如果还没拿到锁,则升级为重量级锁,此时去 OS 操作系统申请资源。