Java各种同步锁详解 1 锁分类概述 1.1 乐观锁 & 悲观锁 根据对同步资源处理策略不同,锁在宏观上分为乐观锁与悲观锁,这只是概念上的一种称呼,Java中并没有具体的实现类叫做乐观锁或者悲观锁。 悲观锁阻塞事务,乐观锁回滚重试,它们各有优缺点,适应场景的不同区别,比如: 实现方式不同: 乐观锁:在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过 悲观锁:依赖Java的synchronized和ReentrantLock等锁去实现。 java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。 Java实现的自旋锁也是一种非公平锁,等待时间最长的线程并不能优先获取锁,可能会产生“线程饥饿”问题。 因此,自旋应该是有限度的。
首发公众号:Rand_cs锁LOCK锁锁,大家应该很熟悉了,用来避免竞争,实现同步。 (); //检验锁lock是否被某CPU锁持有且上锁 popcli(); return r;}关中断下检查锁是否被某 CPU 取走,仔细看检查是否持有锁的条件为两个:一是锁是否被取走,二锁是否由当前 所以在取锁检验锁都要在关中断下进行。 ;休眠锁配了一把自旋锁来保护,原因见后。 当前进程想要获取休眠锁,这个休眠锁就是对象,如果被别的进程取走的话,那么当前进程就取而不得,休眠在休眠锁这个对象上。如果取到了该休眠锁,就将 locked 置为 1,记录取得该锁的进程号。
java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入 值是否一样,一样则更新,否则失败。 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到, 才会转换为悲观锁,如 RetreenLock。 3.自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁 的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋), 等持有锁的线程释放锁后即可立即获取锁 但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合 使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功,占着 XX 不 XX,同时有大量 线程在竞争一个锁,会导致获取锁的时间很长 所以这种情况下我们要关闭自旋锁;自旋锁时间阈值(1.6 引入了适应性自旋锁) 自旋锁的目的是为了占着 CPU 的资源不释放,等到获取到锁立即进行处理。但是如何去选择 自旋的执行时间呢?
悲观锁 & 乐观锁悲观锁:认为多个线程访问同一个共享变量冲突的概率较大,会在每次访问共享变量之前都去真正加锁。Java中的 synchronized 锁 和 ReentrantLock就是悲观锁。 Java中的CAS就是乐观锁,比较当前值(主内存中的值)与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。 读写锁Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写锁。 自旋锁如果获取锁失败,立即再尝试获取锁,无限循环,直到获取到锁为止。第一次获取锁失败,第二次的尝试会在极短的时间内到来,一旦锁被其他线程释放,就能第一时间获取到锁。 同步锁同步锁表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。在java中 synchronized 锁就是同步锁。
可重入锁递归锁,同一个线程,外层函数获得锁,内层的也获得锁。 value.getCode()) { return value.getMsg(); } } return null; } }}CyclicBarrier7个线程是随机的 cyclicBarrier的回调函数 public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()-> System.out.println("完成收集.....")); for (int i = 0; i < 7; i++) { new Thread(() -> { finally { lock.unlock(); } }}多线程的判断用while if只适合两个线程的判断synchronized和lock的区别参考文章存在层次上synchronized: Java
java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。 java中的锁 1.自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁 synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋锁 偏向锁 Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。
一、 Java锁 1.常见的锁有synchronized和Lock() ①synchronized 是jvm层面实现的,可以直接用,不过要锁住某个对象;lock是属于j.u.c包下的接口,用的时候要实现 @lock锁与synchronized相比,lock锁添加一些其他特性,如中断锁等候和定时锁等候。 2.悲观锁与乐观锁 ①悲观锁认为世界是悲观的,当去拿数据的时候就上锁,这样别人想拿这个锁就会阻塞直到拿到锁,传统的数据库用到了这种锁,像行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。 ②乐观锁,认为一般并发是不会发生的,所以不会上锁。 基于CAS(无锁编程)实现,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制(解决ABA问题)。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
7.Lock 同步锁 Lock( 锁 ) 从 JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。 java.util.concurrent.locks.Lock接口 是控制多个线程对共享资源进行访问的工具。 显示锁 Lock - 在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。 Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。 Lock(锁)示例 import java.util.concurrent.locks.ReentrantLock; class A{ private final ReentrantLock lock
Java中导致饥饿的原因: 高优先级线程吞噬所有的低优先级线程的CPU时间。 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。 在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 java中的Compare and Swap即CAS ,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败, 从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。
我们都知道在 Java 中为了保证一些操作的安全性,就会涉及到使用锁,但是你对 Java 的锁了解的有多少呢?Java 都有哪些锁?以及他们是怎么实现的,今天了不起就来说说关于 Java 的锁。 在 Java 中,乐观锁通常是通过版本号、时间戳或其他状态信息来实现的。以下是乐观锁在 Java 中的一些常见实现方式: 版本号机制: 数据表中增加一个“版本号”字段。 读取数据时,同时读取版本号。 ReentrantLock类: Java的java.util.concurrent.locks.ReentrantLock类提供了重入锁的实现,这是一种悲观锁。 递归锁 Java中的递归锁(ReentrantLock)是java.util.concurrent.locks包下提供的一种可重入的互斥锁,它是悲观锁的一种实现。 读写锁 Java中的读写锁(ReadWriteLock)是一种允许多个读线程和单个写线程访问共享资源的同步机制。
synchronized关键字,我们一般称之为“同步锁”,用它来修饰需要同步的方法和需要同步代码块,默认是当前对象作为锁的对象。 在用类修饰synchronized时(或者修饰静态方法),默认是当前类的Class对象作为锁的对象,故存在着方法锁、对象锁、类锁这样的概念。 先给出以下代码感受下代码执行的时候为什么需要同步? 其他线程打不开这个锁,只能在外边等该线程释放掉该锁,一般都都是执行玩所有代码逻辑主动释放锁),表示此方法是当前线程独占的,对应到上述业务中就是一次只能有一个队列报数。 numberOff(); }, "队列" + i).start(); } } } 代码执行结果如下: 最后用线程池将上述代码写一下: package ioo; import java.util.concurrent.ExecutorService ; import java.util.concurrent.Executors; class SynchronizedExample { protected static int num =
由于具体业务场景的需求,需要保证数据在分布式环境下的正确更新,所以研究了一下Java中分布式锁的实现。 Java分布式锁的实现方式主要有以下三种: 数据库实现的乐观锁 Redis实现的分布式锁 Zookeeper实现的分布式锁 其中,较常用的是前两种方式,但是数据库实现方式需要较多的数据库操作,所以最终选择的是用 第一,Redis锁需要有一个超时时间,这样即便某个持有锁的节点挂了,也不到导致其他节点死锁,保证每个锁有一个UniqueId;第二,每个锁需要有一个UniqueId,确保当一个线程执行完一个任务去释放锁的时候释放的一定是自己的锁 ,否则可能存在一种场景,就是一个线程释放锁的时候,它的锁可能已经超时被释放了,而因为缺少一个UniqueId,它却释放了另一个线程的锁 基于以上两点的考虑,分别设计了获取锁和释放锁的api。 若做check的时候,检查得到确实是这个锁的UniqueId,但是在执行del方法之前,这个锁已经超时,然后新的线程也已经获取到锁了,那么del删掉的锁,便不是自己的锁,而是下一个线程的锁。
参考链接: 了解Java中的类和对象 前言 之前看到过一篇文章,写的就是Java关键字synchronized的类锁和对象锁,今天想重温一下无奈发现文章已经搜索不到,百度之大部分都是重复的那么几篇文章 于是重写一篇博客介绍 synchronized 类锁 对象锁。 Java原生提供了 synchronized 关键字用于多线程编程,但往往入门使用者在发现使用情况与预期有差别,可阅读此文章。 Java的 synchronized 锁的是对象,也只锁对象: 对象锁是基于对堆内存内对象的头部加锁信息; 类锁是基于对类对应的 java.lang.Class对象加锁信息; 特别的, synchronized 例如 ReentrantLock是基于Java关键字volatile和CPU的CAS机制来实现的,若有知晓可在留言区告知一二 *) 类锁原理及为何类锁完全互斥 可优先阅读该篇文章图解Java类加载机制 想获得一个Java的对象,则需要先获得Java的一个类,这便是Java的类加载。
明确java线程切换的代价,是理解java中各种锁的优缺点的基础之一。 基础知识之三:markword 在介绍java锁之前,先说下什么是markword,markword是java对象数据结构中的一部分,要详细了解java对象的结构可以点击这里,这里只做markword的详细介绍 了解了markword结构,有助于后面了解java锁的加锁解锁过程; 小结 前面提到了java的4种锁,他们分别是重量级锁、自旋锁、轻量级锁和偏向锁, 不同的锁有不同特点,每种锁只有在其特定的场景下 偏向锁 Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。 7. 如果自旋失败,则升级为重量级锁。
今天我们来聊下线程中的悲观锁和乐观锁,首先提到"悲观锁","乐观锁"提到这两个名词,大家可能会先想到数据库。注意啦,我们这里讲的是多线程中的锁,而不是数据库中的锁(没听过的童鞋,可以百度了解下。 大概思想同线程中的悲乐锁思想差不多)。在Java中,常用Api提供的锁就是synchronized和lock,以及CAS。不知道大家有没有这样的疑惑,我什么场景下用哪把锁最为合适。 ? 这里最典型的是java.util.concurrent并发包中的递增操作就通过CAS自旋实现的。 在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。 ? 总结: 这里我们可以得出悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。不过从jdk1.8之后java已经对synchronized做了优化,性能上有了大幅度的提升。
锁的实现方式 Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类。 synchronized 关键字是最基本也是最常见的一种同步方式。 自适应自旋锁 自适应自旋锁是对自锁锁的一种优化。当一个线程自旋后成功获得了锁,那么下次自旋的次数就会增加。因为虚拟机认为,既然上次自旋期间成功拿到了锁,那么后面的自旋会有很大几率拿到锁。 因为一个是类锁另一个是对象锁。类锁和对象锁是两个不一样的锁,控制着不同的区域,它们互不干扰。 Java会在位置3和位置4之间插入一个获取屏障,在位置18和19之间插入一个释放屏障,这两个屏障保证临界区内的任何操作都不会被指令重排序到临界区之外。加上锁的排他性,临界区内的操作便具有了原子性。 总结: 同步操作的实现,需要给对象关联一个互斥体,这个互斥体就可以叫做锁 锁的作用是,保证同一竞争资源在同一时刻只会有一个线程占有 Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类
互斥锁与读写锁 其实是独享锁与共享锁具体说法;互斥锁Java中实现就是ReentrantLock,而读写锁Java实现是ReadWriteLock。 分段锁 实质上是一种锁的策略,并不是具体的锁。 同步代码一直被一个线程访问,线程自动获取锁。 Java偏向锁是Java6引入的一项多线程优化。 Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互。 多个线程在不同时间段请求同一把锁,也就是没有竞争的情况下,Java虚拟机就会采用轻量级锁,来避免重量级锁阻塞以及重复唤醒。 重量级锁 它是Java中的基础锁,在这种状态下,Java虚拟机会阻塞加锁失败的线程,并且在目标锁被释放的时候,唤醒这些线程。Java中synchronized就是一种重量级锁。
今天说一说java 悲观锁[Java怎样解决高并发],希望能够帮助大家进步!!! 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。 在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 以 java.util.concurrent 中的 AtomicInteger 为例,看一下在不使用锁的情况下是如何保证线程安全的。 int value; 3 4 public final int get() { 5 return value; 6 } 7 8 public
最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 乐观锁适用于多读的应用类型,乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 3.典型应用: java jdk并发包中的ReentrantLock可以指定构造函数的boolean类型来创建公平锁和非公平锁( 我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现 Java线程锁 详细可以参考:高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景 本文标题:最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 转载请保留页面地址:http
在Java环境中,synchronized 和ReentrantLock都是可重入锁。 读写锁:ReadWriteLock 在Java中通过Lock接口及对象可以方便地为对象加锁和释放锁,但是这种锁不区分读写,叫做普通锁。为提高性能,Java提供了读写锁。 在Java中,通过读写锁的接口 java.util.concurrent.locks.ReadWriteLock 的实现类 ReentrantReadWriteLock来完成对读写锁的定义与使用。 Java并发包提供的加锁模式分为独占锁与共享锁。 随着锁竞争越来越激烈,锁可能从偏向锁升级到轻量级锁,再升级到重量级锁。但在Java中锁只单向升级,不会降级。