一篇搞懂Synchronized与Lock的区别

本文详细比较了Java中的Synchronized和Lock在语法、功能(如公平锁、可打断、可超时和多条件变量)以及性能方面的差异,帮助理解并发编程中的这两种重要机制。

引言

并发编程是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的性能更好。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值