iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >详解java中各类锁的机制
  • 143
分享到

详解java中各类锁的机制

2024-04-02 19:04:59 143人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录前言1. 乐观锁与悲观锁2. 公平锁与非公平锁3. 可重入锁4. 读写锁(共享锁与独占锁)6. 自旋锁7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁前言 总结java常见的锁

前言

总结java常见的锁

区分各个锁机制以及如何使用

使用方法 锁名
考察线程是否要锁住同步资源 乐观锁和悲观锁
锁住同步资源后,要不要阻塞 不阻塞可以使用自旋锁
一个线程多个流程获取同一把锁 可重入锁
多个线程公用一把锁 读写锁(写的共享锁)
多个线程竞争要不要排队 公平锁与非公平锁

1. 乐观锁与悲观锁

悲观锁:不能同时进行多人,执行的时候先上锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁

乐观锁:通过版本号一致与否,即给数据加上版本,同步更新数据以及加上版本号。不会上锁,判断版本号,可以多人操作,类似生活中的抢票。每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务

(乐观锁可以使用版本号机制和CAS算法实现)

通过具体案例演示悲观锁和乐观锁

在redis框架

执行multi之前,执行命令watch

具体格式如下


watch key1 [key2]

具体代码格式如下


127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set add 100
OK
127.0.0.1:6379> watch add
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby add 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 120
127.0.0.1:6379> 

flushdb是清空数据库

但如果在另一个服务器上,输入exec,会显示出错

因为用的是乐观锁,被修改了之后版本会发生改变

总的来说:

悲观锁:单独每个人完成事情的时候,执行上锁解锁。解决并发中的问题,不支持并发操作,只能一个一个操作,效率低

乐观锁:每执行一件事情,都会比较数据版本号,谁先提交,谁先提交版本号

2. 公平锁与非公平锁

公平锁:先来先到

非公平锁:不是按照顺序,可插队

  • 公平锁:效率相对低
  • 非公平锁:效率高,但是线程容易饿死

通过这个函数Lock lock = new ReentrantLock(true);。创建一个可重入锁,true 表示公平锁,false 表示非公平锁。默认非公平锁

通过查看源码

带有参数的ReentrantLock(true)为公平锁

ReentrantLock(false)为非公平锁

主要是调用NonfairSync()与FairSync()


public ReentrantLock() {
        sync = new NonfairSync();
    }

    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

具体其非公平锁与公平锁的源码

查看公平锁的源码


static final class FairSync extends Sync {
   private static final long serialVersionUID = -3000897897090466540L;

  
  final boolean initialTryLock() {
   Thread current = Thread.currentThread();
   int c = getState();
   if (c == 0) {
   if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
     setExclusiveOwnerThread(current);
      return true;
    }
    } else if (getExclusiveOwnerThread() == current) {
      if (++c < 0) // overflow
          throw new Error("Maximum lock count exceeded");
         setState(c);
         return true;
       }
    return false;
}

通过代码实例具体操作


//第一步  创建资源类,定义属性和和操作方法
class LTicket {
    //票数量
    private int number = 30;

    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock(true);
    //卖票方法
    public void sale() {
        //上锁
        lock.lock();
        try {
            //判断是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    //创建三个线程
    public static void main(String[] args) {

        LTicket ticket = new LTicket();

new Thread(()-> {
    for (int i = 0; i < 40; i++) {
        ticket.sale();
    }
},"AA").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}

结果截图如下

都是A线程执行,而BC线程都没执行到,出现了非公平锁

具体改变其设置可以通过可重入锁中的一个有参构造方法

修改代码为private final ReentrantLock lock = new ReentrantLock(true);

代码截图为

3. 可重入锁

可重入锁也叫递归

而且有了可重入锁之后,破解第一把之后就可以一直进入到内层结构


Object o = new Object();
new Thread(()->{
    synchronized(o) {
        System.out.println(Thread.currentThread().getName()+" 外层");

        synchronized (o) {
            System.out.println(Thread.currentThread().getName()+" 中层");

            synchronized (o) {
                System.out.println(Thread.currentThread().getName()+" 内层");
            }
        }
    }

},"t1").start();

synchronized (o)代表锁住当前{ }内的代码块

以上都是synchronized锁机制

下面讲解lock锁机制


public class SyncLockDemo {

    public synchronized void add() {
        add();
    }

    public static void main(String[] args) {
        //Lock演示可重入锁
        Lock lock = new ReentrantLock();
        //创建线程
        new Thread(()->{
            try {
                //上锁
                lock.lock();
                System.out.println(Thread.currentThread().getName()+" 外层");

                try {
                    //上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+" 内层");
                }finally {
                    //释放锁
                    lock.unlock();
                }
            }finally {
                //释放做
                lock.unlock();
            }
        },"t1").start();

        //创建新线程
        new Thread(()->{
            lock.lock();
            System.out.println("aaaa");
            lock.unlock();
        },"aa").start();
        }
 }

在同一把锁中的嵌套锁,内部嵌套锁没解锁还是可以输出,但是如果跳出该线程,执行另外一个线程就会造成死锁

要把握上锁与解锁的概念,都要写上

4. 读写锁(共享锁与独占锁)

读锁是共享锁,写锁是独占锁

  • 共享锁的一种具体实现
  • 读写锁管理一组锁,一个是只读的锁,一个是写锁。

读写锁:一个资源可以被多个读线程访问,也可以被一个写线程访问,但不能同时存在读写线程,读写互斥,读读共享(写锁独占,读锁共享,写锁优先级高于读锁)

读写锁ReentrantReadWriteLock

读锁为ReentrantReadWriteLock.ReadLock,readLock()方法

写锁为ReentrantReadWriteLock.WriteLock,writeLock()方法

创建读写锁对象private ReadWriteLock rwLock = new ReentrantReadWriteLock();

写锁 加锁 rwLock.writeLock().lock();,解锁为rwLock.writeLock().unlock();

读锁 加锁rwLock.readLock().lock();,解锁为rwLock.readLock().unlock();

案例分析:

模拟多线程在map中取数据和读数据

完整代码如下


//资源类
class MyCache {
    //创建map集合
    private volatile Map<String,Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }

        TimeUnit.MICROSECONDS.sleep(300);

        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

5. 互斥锁

互斥锁是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性


pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//创建互斥锁并初始化

pthread_mutex_lock(&mutex);//对线程上锁,此时其他线程阻塞等待该线程释放锁

//要执行的代码段

pthread_mutex_unlock(&mutex);//执行完后释放锁

6. 自旋锁

查看百度百科的解释,具体如下 :

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名

通俗的来说就是一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务。

其特点:

  1. 持有锁时间等待过长,消耗CPU
  2. 无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题
  3. 自旋锁不会使线程状态发生切换,处于用户态(不会到内核态进行线程的状态转换),一直都是活跃,不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

其模拟算法如下


do{
	b=1;
	while(b){
		lock(bus);
		b = test_and_set(&lock);
		unlock(bus);
	}
	//临界区
	//lock = 0;
	//其余部分
}while(1)

7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁

  • 无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功
  • 偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价
  • 轻量级锁:锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能
  • 重量级锁:线程并发加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,还有线程要访问

以上就是详解java中各类锁的机制的详细内容,更多关于java锁的机制的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解java中各类锁的机制

本文链接: https://www.lsjlt.com/news/160775.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解java中各类锁的机制
    目录前言1. 乐观锁与悲观锁2. 公平锁与非公平锁3. 可重入锁4. 读写锁(共享锁与独占锁)6. 自旋锁7. 无锁 / 偏向锁 / 轻量级锁 / 重量级锁前言 总结java常见的锁...
    99+
    2022-11-12
  • java中各类锁的机制是什么
    这篇文章给大家分享的是有关java中各类锁的机制是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。前言总结java常见的锁区分各个锁机制以及如何使用使用方法锁名考察线程是否要锁住同步资源乐观锁和悲观锁锁住同步资...
    99+
    2023-06-22
  • linux中各种锁机制的使用与区别详解
    前言: 相信需要了解这方面的知识的小伙伴,已经基本对进程间通信和线程间通信有了一定了解。例如,进程间通信的机制之一:共享内存(在这里不做详解):多个进程可同时访问同一块内存。如果不对访问这块内存的临界区进行互斥或者同步,...
    99+
    2022-06-04
    linux锁机制 linux中断机制 linux内核锁机制
  • Java的锁机制:synchronized和CAS详解
    目录一为什么要用锁二synchronized怎么实现的三CAS来者何人四synchronized和CAS孰优孰劣轻量级锁重量级锁总结提到Java的知识点一定会有多线程,JDK版本不断...
    99+
    2022-11-12
  • java synchronized 锁机制原理详解
    目录前言: 1、synchronized 的作用:2、synchronized 底层语义原理:3、 synchronized 的显式同步与隐式同步:3.1、syn...
    99+
    2022-11-12
  • 深入解析MySQL中的各种锁机制
    MySQL 各种锁详解一、引言在并发访问中,数据库需要使用锁来保护数据的一致性和完整性。MySQL 提供了多种类型的锁,包括共享锁、排他锁、意向共享锁、意向排他锁等。本文将使用具体的代码示例介绍并解析这些锁的使用方式和特点。二、共享锁(Sh...
    99+
    2023-12-21
    MySQL - 事务 - 行锁 - 表锁
  • mysql的锁机制详解
    这段时间一直在学习mysql数据库。项目组一直用的是oracle,所以对mysql的了解也不深。本文主要是对mysql锁的总结。 Mysql的锁主要分为3大类:    表级锁:存储引擎为Myisam。锁住整个表,特点是开销小,加锁快,锁定力...
    99+
    2021-08-13
    mysql的锁机制详解
  • 详解Java中String类的各种用法
    目录一、创建字符串二、字符、字节与字符串的转换1.字符与字符串的转换2.字节与字符串的转换三、字符串的比较1.字符串常量池2.字符串内容比较四、字符串查找五、字符串替换六、字符串拆分...
    99+
    2022-11-12
  • 一文详解Java中的类加载机制
    目录一、前言二、类加载的时机2.1 类加载过程2.2 什么时候类初始化2.3 被动引用不会初始化三、类加载的过程3.1 加载3.2 验证3.3 准备3.4 解析3.5 初始化四、父类...
    99+
    2022-11-13
  • Java中的各种锁详细介绍
    这篇文章主要讲解了“Java中的各种锁详细介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的各种锁详细介绍”吧!锁有什么作用呢说了这么多还是不清楚锁到底有什么用处这一点就要深思我...
    99+
    2023-06-16
  • 详细介绍Java中的各种锁
    一、一张图了解21种锁 二、乐观锁 应用 CAS 思想 一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改 实现 写数据...
    99+
    2022-11-12
  • java之JVM各类机制的示例分析
    这篇文章将为大家详细讲解有关java之JVM各类机制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3. 客户...
    99+
    2023-06-14
  • Java并发编程之显式锁机制详解
            我们之前介绍过synchronized关键字实现程序的原子性操作,它的内部也是一种加锁和解锁机制,是一种声明式的编程方式,我们只需要对方法或者代码块进行声...
    99+
    2023-05-30
    java 并发编程 显式锁机制
  • 一文详解Go语言中的锁机制
    作为一门高并发的编程语言,Go语言的并发控制机制非常重要。其中最常用的机制之一就是锁机制。本文将介绍如何在Go语言中实现锁机制。Go语言的锁在Go语言中,最常用的锁是互斥锁(Mutex)。互斥锁是一种特殊的二进制信号量,用于控制对共享资源的...
    99+
    2023-05-14
  • Java中的反射机制详解
    目录一、什么是反射?二、为什么要用反射三、Class类四、获取Class类对象的四种方式五.通过反射构造一个类的实例①使用Class.newInstance②通过反射先获取构造方法再...
    99+
    2022-11-12
  • Mysql InnoDB的锁定机制实例详解
    1.InnoDB的锁定机制 InnoDB存储引擎支持行级锁,支持事务处理,事务是有一组SQL语句组成的逻辑处理单元,他的ACID特性如下: 原子性(Atomicity): 事务具有原子不可分割的特性,要么一起...
    99+
    2022-05-18
    mysql innodb锁定 mysqlinnodb mysql排他锁
  • Java中的锁机制是什么
    今天小编给大家分享一下Java中的锁机制是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Java中的锁机制是保证多线程并...
    99+
    2023-07-05
  • 详解java中的互斥锁信号量和多线程等待机制
    互斥锁和信号量都是操作系统中为并发编程设计基本概念,互斥锁和信号量的概念上的不同在于,对于同一个资源,互斥锁只有0和1 的概念,而信号量不止于此。也就是说,信号量可以使资源同时被多个线程访问,而互斥锁同时只能被一个线程访问互斥锁在java中...
    99+
    2023-05-31
    java 互斥锁 信号量
  • 详解JAVA如何实现乐观锁以及CAS机制
    目录前言问题引入悲观锁解决乐观锁解决乐观锁改进CAS机制总结前言 生活中我们看待一个事物总有不同的态度,比如半瓶水,悲观的人会觉得只有半瓶水了,而乐观的人则会认为还有半瓶水呢。很多技...
    99+
    2022-12-08
    JAVA乐观锁 CAS机制 JAVA乐观锁 JAVA CAS
  • 详解MySql中InnoDB存储引擎中的各种锁
    目录什么是锁InnoDB存储引擎中的锁锁的算法行锁的3种算法幻像问题锁的问题脏读不可重复读丢失更新死锁什么是锁 现实生活中的锁是为了保护你的私有物品,在数据库中锁是为了解决资源争抢的...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作