Java synchronized 关键字 可以将一个代码块或一个方法标记为同步代码块。同步代码块是指同一时间只能有一个线程执行的代码,并且执行该代码的线程持有同步锁。synchronized
关键字可以作用于
一个代码块
一种方法
当一个方法或代码块被声明为synchronized时,如果一个线程正在执行该synchronized 方法或代码块,其他线程会被阻塞,直到持有同步锁的线程释放。根据锁定的范围可以分为
类级别的锁可以防止多个线程在运行时同时进入该类所有实例化对象的 synchronized代码块中。
对象级别的锁可以防止多个线程在运行时同时进入当前(或某一个)实例化对象的 synchronized代码块中。
对象级别的同步锁:当我们想要在多线程环境下同步执行一个非静态方法或非静态代码块时,在类的方法或代码块加上synchronized关键字,可以保证对象实例级别数据的线程安全。(比较后文的类级别的同步锁,回头来理解这句话)
对象级别的加锁的代码如下,如:在方法上加锁,锁对象为当前类的实例化对象
public class DemoClass{ public synchronized void demoMethod(){} }
如:为代码块加锁,锁对象为this对象
public class DemoClass{ public void demoMethod(){ synchronized (this){ //同步代码块 } } }
如:为代码块加锁,锁对象为我们创建的任意一个对象。不要使用非final的成员变量作为同步锁对象,因为非final成员变量可以被重新赋值,导致不同的线程使用不同的对象作为锁,达不到同步锁定的效果。
public class DemoClass{ //注意这里的关键字final非常重要,看说明 private final Object lock = new Object(); public void demoMethod(){ synchronized (lock){ //同步代码块 } } }
类级别的锁可以防止多个线程在运行时进入该类所有实例化对象的 "synchronized块中。也就是说如果运行时有100个DemoClass
的实例,那么每次只有一个线程能够在任何一个实例中执行demoMethod()
,所有其他实例的所有其他线程都被锁定。
为了保障静态数据线程安全,应该使用类级别的锁定。我们知道static关键字将方法的数据关联到类的级别上,所以在静态方法上使用锁。
静态方法加锁,对该类所有的实例化对象生效
public class DemoClass{ //静态方法加锁,对该类所有的实例化对象生效 public synchronized static void demoMethod(){ } }
获取 .class类的引用,类级别的锁
public class DemoClass{ public void demoMethod(){ //获取 .class类的引用,类级别的锁,对该类所有的实例化对象生效 synchronized (DemoClass.class){ //同步代码块 } } }
使用静态对象的锁,类级别的锁
public class DemoClass{ //静态对象,类级别,注意这里的关键字final非常重要 private final static Object lock = new Object(); public void demoMethod(){ //使用静态对象的锁,类级别锁,对该类所有的实例化对象生效 synchronized (lock){ //同步代码块 } } }