synchronized特性
synchronized使用
synchornized锁机制
1.synchronized的特性
- 根据六大锁策略,synchronized刚开始是轻量级锁,后面可能会升级为重量级锁;
- 所以即是轻量级锁也是重量级锁;
- 所以既是自旋锁也是挂起等待锁,轻量级锁由自旋方式实现,重量级锁由系统调用API,挂起等待锁实现;
- 是一种非公平锁;
- 是一种可重入锁;
- 是互斥锁,不是可读锁;
2.synchronized使用
synchronized是一个保证线程安全的关键字,常用来修饰方法和代码块,确保同一个时刻只有一个线程被修饰的代码在执行,防止数据被多个线程获取修改带来的线程不安全;
使用sunchronized需要指定一个变量或者对象作为标志,意在说明两个或多个代码块或者方法竞争的是同意一把锁,只有有竞争才有锁存在的意义;
1.一般用法(多线程)
public class Example {
public static void main(String[] args) throws InterruptedException {
Object lock=new Object();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock){
System.out.println("线程1");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock){
System.out.println("线程2");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
由于两个线程的方法竞争的是同意一把锁(一样的标志对象),所以无论哪个线程先执行,只有另一个线程synchronized代码块完全执行完,另一个线程获取到锁才能继续执行;
2.用于代码块
2.1 锁任意对象
public class test1 {
public static void main(String[] args) {
Object lock=new Object();
Thread t=new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
}
}
});
}
}
2.2 锁当前对象
public class test2 {
public void example(){
synchronized(this){//this指的是调用当前方法的对象
}
}
}
由于方法种只有一个synchronized一个代码块,所以可以将synchronized夹在public之后
public class test2 {
public synchronized void example(){
}
}
拓展
public class test2 { public synchronized void example1(){ } public synchronized void example2(){ } }
由于代码中的两个方法,锁的都是调用该方法的对象,所以同一个线程同一个时刻只能执行一个方法,称之为同步方法
3.用于方法
3.1 直接用于普通方法 锁:调用该方法的对象(test2的实例化对象)
public class test2 {
public synchronized void example1() {
}
}
3.2直接用于静态方法 锁:test2的类对象
public class test2 {
public synchronized static void example1() {
}
}
wait()
一般配合synchronized使用,lock.wait(),执行之后让该线程挂起,进入等待队列并释放锁,等待被唤醒;
notify()/notifyAll()
lock.notify()唤醒该对象挂起等待队列的任意一个的线程,并尝试让它去获取锁;
lock.notifyAll()唤醒该对象挂起等待队列的所有线程,并让他们去竞争锁,没拿到锁的重新进入等待队列;
3.synchronized锁机制
synchronized加锁的过程一般分为四步,无锁->偏向锁->轻量级锁->重量级锁
偏向锁
第一个获取该锁的线程,进入偏向锁状态
偏向锁并不是真的给该线程加锁,只是加一个偏向锁的标记,记录该锁属于哪个线程;如果后续没有其他线程尝试获取该锁,就一直是偏向锁状态,不消耗加锁的开销;如果有其他线程尝试加锁,由于该锁已经记录属于哪个线程,这个时候锁就升级成为轻量级锁;
轻量级锁
如果由其他线程的锁竞争,就会取消偏向锁状态,进入轻量级锁;
轻量级锁一般用自旋锁方式实现,通过CAS机制实现;
- 检查该线程是否获得该锁
- 获得锁,加锁成功
- 没有获得锁,加锁失败
并不会一直循环检查,到了一定时间/次数还没有获得锁,就会变成重量级锁,所以也成为自适应的轻量级锁;
重量级锁
升级成为轻量级锁之后还没有获得锁,就会继续升级成为重量级锁;
重量级实际就是进入内核,调用内核提供的mutex;
- 进入内核,进行加锁;
- 判断该锁是否被占用;
- 锁没有被占用,加锁成功,返回用户态;
- 锁被占用,加锁失败,该线程进入等待队列,等待被唤醒;
- 等待该锁被释放,操作系统想起唤醒该线程,该线程才去尝试获取锁;
synchronized锁优化
具体可以参考我的另一篇博客,锁策略,CAS和synchronized的锁优化