引言
并发编程是Java基础中的重点同时也是难点,相信大家遇见锁要不就是摸不着头脑,要不就是只会使用Synchronized悲观锁。本篇文章为大家讲解Synchronized与Lock的区别。
语法不同
在语法方面,synchronized是关键字,源码保存在jvm中,底层使用C++语言实现。
而Lock是接口,源码由jdk提供,java语言实现。
在使用synchronized的时候退出同步代码块锁会自动释放,使用Lock时,必须手动调用unlock方法。
功能不同
synchronized与lock都属于悲观锁,同时具备互斥、同步、锁重入功能。
而Lock提供了许多额外的功能。以下介绍Lock额外的功能。
Lock的额外功能
公平锁
以Lock的实现类ReenTranLock来看,当创建ReenTranLock时传入的参数为true是,会创建一个公平锁,查看Sync发现继承了AbstractQueuedSynchronized,这就说明Lock底层使用AQS+CAS来实现的,传入的参数为空或者false则创建非公平锁。
可打断
首先声明一个ReenTranLock锁,然后创建一个线程t1并且调用可打断的方法,首先主线程获取锁并且睡眠3秒,之后开启线程t1,观察控制台。
package cn.cxj.mq.thread;
import java.util.concurrent.locks.ReentrantLock;
public class ReentranLockTest {
static ReentrantLock lock= new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
lockInterrrupt();
}
public static void lockInterrrupt() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
//开启可打断的锁功能
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("在等待获取锁的时候被打断!!!");
return;
}
try {
System.out.println(Thread.currentThread().getName() + "拿到锁!!!");
} finally {
lock.unlock();
}
}, "T1");
//在主线程中获取锁
lock.lock();
System.out.println("主线程获取到锁!!");
t1.start();
//主线程拿到锁睡眠3秒
try {
Thread.sleep(3000);
t1.interrupt();
System.out.println("t1被打断");
}finally {
lock.unlock();
}
}
}
观察结果
可超时
首先声明一个ReenTranLock锁,然后创建一个线程t1并且尝试获取锁传入等待时间例如4秒,首先主线程获取锁并且睡眠3秒,之后开启线程t1,观察控制台。
package cn.cxj.mq.thread;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class ReentranLockTest {
static ReentrantLock lock= new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
canTimeOut();
}
public static void canTimeOut() throws InterruptedException {
Thread t1 = new Thread(() -> {
//尝试获取锁,成功返回true,失败返回false
try {
if (!lock.tryLock(4, TimeUnit.SECONDS)) {
System.out.println("t1线程获取锁失败!!!1");
return;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("t1线程成功获取锁!!!!!");
lock.unlock();
}, "t1");
//主线程获取锁
lock.lock();
System.out.println("主线程获取了锁!!!!");
t1.start();
Thread.sleep(3000);
lock.unlock();
}
}
查看结果
可以设置多条件变量
首先声明一个ReenTranLock锁和调用newCondition创建条件,然后创建一个线程t1并且尝试获取锁调用条件进入等待,线程t2同理,线程t3,唤醒t1,t2,最后观察控制台。
package cn.cxj.mq.thread;
import org.springframework.retry.backoff.ThreadWaitSleeper;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReentranLockTest {
static ReentrantLock lock= new ReentrantLock();
//创建条件
static Condition c1=lock.newCondition();
static Condition c2=lock.newCondition();
public static void main(String[] args) throws InterruptedException {
conditionTest();
}
public static void conditionTest() throws InterruptedException {
new Thread(() -> {
lock.lock();
try {
//进入等待,释放锁
c1.await();
System.out.println(Thread.currentThread().getName()+"获取锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock();
try {
//进入等待
c2.await();
System.out.println(Thread.currentThread().getName()+"获取锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}, "t2").start();
new Thread(() -> {
lock.lock();
try {
//唤醒t1,t2
c1.signal();
c2.signal();
System.out.println(Thread.currentThread().getName()+"获取锁");
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}, "t3").start();
}
}
查看结果
性能方面
synchronized目前进行了很多优化,实现了轻量级锁、偏向锁,在竞争小或者没有竞争的情况下,性能还是很不错的。
但是在竞争激烈的时候实现Lock接口的锁往往比synchtonized的性能更好。
本文详细比较了Java中的Synchronized和Lock在语法、功能(如公平锁、可打断、可超时和多条件变量)以及性能方面的差异,帮助理解并发编程中的这两种重要机制。





455

被折叠的 条评论
为什么被折叠?



