当前位置: 首页 > article >正文

synchronized锁与lock锁的区别

引言

在学习多线程时,当时为了解决线程并发问题,曾有两种锁,一种是synchronized同步块,同步方法,一种就是Lock锁,那么这两种锁之间有什么区别?谁更好用呢?

synchronized

同步方法(synchronized)

本质:队列+锁

  • 由于我们可以通过private关键字来保证数据对象只被方法访问,所以只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。

     public synchronized void method(int args){}
  • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就会独占该锁,直到该方法返回才释放锁,后面被堵塞的线程才能获得这个锁,继续执行

    缺陷:若将一个大的方法申明为synchronized将会影响效率

弊端

方法里面需要修改的内容才需要锁,锁得太多,浪费资源

同步块
  • 同步块:synchronized(Obj){}

  • Obj称之为 同步监视器

    • obj可以是任何对象,但是推荐使用共享资源作为监视器

    • 同步方法中无需指定监视器,因为同步方法的监视器就是this,就是这个对象本身,或者是class

  • 同步监视器的执行过程

    1. 第一个线程访问,锁定同步监视器,执行其中代码

    2. 第二个线程访问,发现同步监视器被锁定,无法访问

    3. 第一个线程访问完毕,解锁同步监视器

    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

案例展示:

package com.lyc.synchornized;

import java.awt.dnd.DragGestureEvent;

//不安全的取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(700,"奖学金");
        Bank MIng = new Bank(account,100,"小明");
        Bank Teacher = new Bank(account,50,"老师");
        MIng.start();
        Teacher.start();
    }
}
//账户
class Account{
     int money;
     String name;
    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }

}
//银行:模拟取款
class Bank extends Thread{
    private Account account;//账户
    private int DrawingMoney;//取了多少钱
    private int nowMoney;// 现在手里有多少钱
    public Bank(Account accout,int DrawingMoney,String name){
        super(name);
        this.account = accout;
        this.DrawingMoney = DrawingMoney;
    }
    //取钱
    //synchronized 默认锁的是this
    //所以要使用同步块

    @Override
    public  void run() {
        //锁的对象就是变化的量 需要增删改
        synchronized (account){
            if (account.money<DrawingMoney){
                System.out.println(Thread.currentThread().getName()+"取钱失败,余额不足");
                return;
            }
            //sleep可以放大问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //判断有没有钱

        //卡内余额 = 余额-你取的钱
        account.money = account.money - DrawingMoney;
        //你手里的钱
        nowMoney = nowMoney + DrawingMoney;
        System.out.println(account.name+"余额为"+account.money);
        //Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName()+"手里的钱为"+nowMoney);
    }

}

Lock

我们直接去jdk文档中去找

 Lock接口实现类主要分为三类:

ReentrantLock(可重入锁【常用】) , ReentrantReadWriteLock.ReadLock (读锁), ReentrantReadWriteLock.WriteLock(写锁)

我们再去源码中看看:

 发现在ReentrantLock中还有公平锁与非公平锁之分

公平锁:顾名思义,十分公平,讲究先来后到,不能插队

非公平锁:十分不公平:可以插队(默认)

那么如何使用Lock锁呢?

lock三部曲
1.new ReentrantLock()
2.lock.lock();//加锁
3.lock.unlock();//解锁

代码示例:

package com.lyc.demo01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket2 ticket = new Ticket2();
        //@FunctionalInterface 函数式接口,jdk1.8新特性 lambda表达式 (参数)->{代码}
        new Thread(() ->
        {for (int i = 0; i < 40; i++)  ticket.sale();},"A").start();
        new Thread(() ->
        {for (int i = 0; i < 40; i++)  ticket.sale();},"B").start();
        new Thread(() ->
        {for (int i = 0; i < 40; i++)  ticket.sale();},"C").start();
        new Thread(() ->
        {for (int i = 0; i < 40; i++)  ticket.sale();},"D").start();

    }
}
//lock三部曲
//1.new ReentrantLock()
//2.lock.lock();//加锁
//3.lock.unlock();//解锁
class Ticket2{
    private int number  = 50;
    //买票的方式
    Lock lock = new ReentrantLock();//可重入锁
    public void sale() {
        lock.lock();//加锁

        try {
            //业务代码
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余了" + number);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();//解锁
        }

    }
}

区别(重点)

1.Synchronized是内置的Java关键字,lock是一个Java类

2.Synchronized无法判断锁的状态,Lock可以判断是否获得了锁

3.Synchronized 会自动释放锁(同步块结束就释放了)lock必须手动释放锁,如果不释放锁,就会造成死锁

4.Synchronized 线程1(同步块 阻塞) 线程2(就会一直等待) Lock锁就不会一直等待(try lock()

原因:Lock实现提供了使用synchronized方法和语句的附加功能,通过提供非阻塞尝试来获取锁( tryLock() ),尝试获取可被中断的锁( lockInterruptibly()) ,以及尝试获取可以超时( tryLock(long, TimeUnit) )。

5.Synchronized 可重入锁,不可中断,非公平 Lock ,可重入锁,可以中断,非公平(可以自己设置)

6.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

这就是我总结的两者之间的区别,希望能帮助到大家


http://www.kler.cn/a/614811.html

相关文章:

  • JAVA学习*工厂模式
  • Linux课程学习一
  • 【Kafka】深入探讨 Kafka 如何保证一致性
  • 【区块链安全 | 第八篇】多签机制及恶意多签
  • keil的代码美化工具AStyle3.1
  • 【vue】聊一聊嵌套路由使用keep-alive缓存的实现
  • 面向服务架构(SOA)及ESB的作用与特点
  • 2023第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组(真题题解)(C++/Java题解)
  • 算法-前缀和与差分
  • FGSM对抗样本生成算法实现(pytorch版)
  • AI助力高效办公:如何利用AI制作PPT提升工作效率
  • 结构化分析方法 数据流图详解
  • 工业控制系统安全:从漏洞到防线,Python如何成为你的护卫者
  • 图论 岛屿问题 ACM 模式 JS
  • 怎么搭建区块链服务私有云平台
  • C++实现布隆过滤器
  • 创意 Python 爱心代码分享
  • Python常用爬虫库介绍
  • vue3+element plus +el-tree-v2实现树形单选
  • presto任务优化参数