Lock lock = new ReentrantLock();
try{
lock.lock();//加锁操作
}finally{
lock.unlock();
}
使用过后必须释放锁
当我们new一个ReentrantLock对象时,底层会帮我们new一个NonfairSync对象,NonfairSync FairSync都是基于AQS队列实现,AbstractQueuedSynchronizer简称为AQS队列。
它是基于先进先出FIFO实现的等待队列,AQS队列是由Node节点组成的双向连标实现啊的,所有的操作都是在这个AQS队列当中,如果一个线程获取锁就直接成功,如果失败了就将其放入等待队列当中。
1)CAS操作抢占锁,抢占成功则修改锁的状态为1,将线程信息记录到锁当中,返回state=1
2)抢占不成功,tryAcquire获取锁资源,获取成功直接返回,获取不成功,新建一个检点插入到
当前AQS队列的尾部,acquireQueued(node)表示唤醒AQS队列中的节点再次去获取锁
1)获取锁的状态值,释放锁将状态值-1
2)判断当前释放锁的线程和锁中保存的线程信息是否一致,不一致会抛出异常
3)状态只-1直到为0,锁状态值为0表示不再占用,为空闲状态
class Singleton1{
private static Lock lock = new ReentrantLock(true);
public static void test(){
for(int i=0; i<2; i++){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+"获取了锁");
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
new Thread("线程A"){
@Override
public void run() {
test();
}
}.start();
new Thread("线程B"){
@Override
public void run() {
test();
}
}.start();
}
}
运行结果为:
线程A获得了锁
线程B获得了锁
线程A获得了锁
线程B获得了锁
private static Lock lock = new ReentrantLock(true);
给定参数为(true)则为公平锁,哪个线程等待的时间越长,哪个线程获得锁
class Singleton1{
private static Lock lock = new ReentrantLock();
public static void test(){
for(int i=0; i<2; i++){
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+"获取了锁");
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
new Thread("线程A"){
@Override
public void run() {
test();
}
}.start();
new Thread("线程B"){
@Override
public void run() {
test();
}
}.start();
}
}
private static Lock lock = new ReentrantLock();
参数为默认参数,为非公平锁,线程随机获得锁
重入锁实现可重入性原理或机制是:每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。
public class TestDemo10 {
public static synchronized void lock1(){
System.out.println("lock1");
lock2();
}
public static synchronized void lock2(){
System.out.println("lock2");
}
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
TestDemo10 lock = new TestDemo10();
lock.lock1();
}
}.start();
}
}
private static Lock lock = new ReentrantLock();
private static int i = 0;
try{
lock.lock();
lock.lock();
i++;
} finally {
lock.unlock();
lock.unlock();
}
运行不会被阻塞,所以R~~eentrantLock为可重入锁
public class TestDemo10 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread("thread1"){
@Override
public void run() {
try{
if(lock.tryLock(5, TimeUnit.SECONDS)){
TimeUnit.SECONDS.sleep(6);
}else{
System.out.println(Thread.currentThread().getName()+"获取锁失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread thread2 = new Thread("thread2"){
@Override
public void run() {
try{
if(lock.tryLock(5, TimeUnit.SECONDS)){
TimeUnit.SECONDS.sleep(6);
}else{
System.out.println(Thread.currentThread().getName()+"获取锁失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
};
thread1.start();
thread2.start();
}
}
运行结果为:
线程2获取锁失败
因为线程1先获得锁,执行睡眠6秒的操作,睡眠中不会释放掉锁,因此线程2在5秒内没有获取到锁。
class Singleton1{
private Singleton1(){
}
private static volatile Singleton1 instance;
private static Lock lock = new ReentrantLock();
//返回当前唯一一个实例
static Singleton1 getInstance(){
if(instance == null){//判断是否初始化
try{
lock.lock();
if(instance == null){
instance = new Singleton1();
// System.out.println("single has been initialized by "+Thread.currentThread().getName());
}
} finally {
lock.unlock();
}
}
return instance;
}
}
public class TestDemo11{
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println(Singleton1.getInstance());
}
}.start();
}
}
它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,等到释放掉锁或者唤醒后才能继续获得锁。
对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成
便利性:Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:ReenTrantLock优于Synchronized