synchronized和lock的区别
一、共同点
当出现互斥资源时,期望一段代码同时只能一个线程访问,如商品抢购活动,多线程同时操作数据库可能导致数据错误。
二、区别
-
synchronized 是JVM内置的关键字,Lock是java.util.concurrent.Locks 包下的一个接口。
-
synchronized 不管是否出现异常,会自动释放锁;Lock需要手动释放锁,尤其是在出现异常时,一定要在finally中手动释放,否则将出现死锁。
-
synchronized 不可中断,Lock可以中断。
-
synchronized 无法自动获得锁是否成功。
2.1 synchronized 使用
一个类有一个类锁,一个对象有一个对象锁。
synchronized 可修饰以下几种情况。
- 修饰代码块,被修饰的代码块使用{}包含,获得对象锁。
- 修饰方法,作为范围为整个方法,获得对象锁
- 修饰静态方法,获得类锁。
- 修饰类,获得类锁。
1、修饰代码块
一个线程访问一个对象中的synchronized(this)同步代码块时,第一个线程获得锁成功访问,其他线程被阻塞,等待获得锁的线程执行完后自动释放锁。(多个线程必须是同一对象访问同步代码块)
@PostMapping("/testsynchronized")
public ApiResult testsynchronized() throws InterruptedException {
synchronized (this){
System.out.println(Thread.currentThread().getName()+"进入同步代码块");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"同步代码块结束");
}
}
2、修饰方法
作用跟修饰代码块相同,作用范围为整个方法
@PostMapping("/testsynchronized")
public ApiResult testsynchronized() throws InterruptedException {
testsynchronizedMothd();
}
private synchronized void testsynchronizedMothd() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"进入同步方法");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"同步方法结束");
}
3、修饰静态方法
由于静态方法属于类所有,和修饰方法的区别在于同一个类的所有对象访问该方法都将互斥,竞争类锁。
4、修饰类
在方法中使用synchronized(类名.class) 同样活动类锁。
class ClassName {
public void method() {
synchronized(ClassName.class) {
}
}
}
2.2 .Lock的用法
Lock是一个接口,有很多实现类,常用的有ReentrantLock。
Lock一些常用的方法。
-
lock();获得锁,成功则执行同步代码,不成功则阻塞。
private Lock lock = new ReentrantLock(); @PostMapping("/testsynchronized") public ApiResult testsynchronized() throws InterruptedException { lock.lock(); System.out.println(Thread.currentThread().getName()+"上锁成功"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName()+"释放锁"); lock.unlock(); } }
-
tryLock();有返回值,获得锁成功返回true,否则返回false。
private Lock lock = new ReentrantLock(); @PostMapping("/testsynchronized") public ApiResult testsynchronized() throws InterruptedException { if(lock.tryLock()){ System.out.println(Thread.currentThread().getName()+"上锁成功"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName()+"释放锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread().getName()+"获取锁失败"); } }
-
tryLock(long time, TimeUnit unit);有返回值,活动锁成功则返回true,不成功不会马上返回,会在传入的时间内不断的尝试获得锁,期间获得锁成功返回true,如果时间结束都还没活动所则返回false。
private Lock lock = new ReentrantLock(); @PostMapping("/testsynchronized") public ApiResult testsynchronized() throws InterruptedException { boolean tryLock = lock.tryLock(5,TimeUnit.SECONDS); if(tryLock){ System.out.println(Thread.currentThread().getName()+"上锁成功"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName()+"释放锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread().getName()+"获取锁失败"); } }
-
lockInterruptibly(),同样会阻塞,区别在于如果线程在等待,这个线程可以响应中断,即中断线程的等待状态。