那么什么是锁? Java中每个对象都有一个内置锁。当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。 实例: package com.test.java.thread; /** * 死锁 * Created by mrf on 2016/2/26. */ public class DeadLockRisk Thread-1释放了锁 Thread-2得到了锁 Thread-3得到了锁 Thread-2释放了锁 Thread-3释放了锁 Thread-4得到了锁 Thread-4释放了锁 Thread-5得到了锁 7.2.可中断锁 可中断锁:顾名思义,就是可以相应中断的锁。 在Java中,synchronized就不是可中断锁,而Lock是可中断锁。 7.4.读写锁 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。 正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。
简介 上篇已经对锁的属性做了一个简单的介绍,此篇主要针对于不同锁的使用,分析优缺点,方便以后使用锁的时候能选择合适的锁。 可以看下java.util.concurrent包中的AtomicIntege类,看下在不使用锁的情况下是怎么保证线程安全的,以下非标准源码,按照原理写的简易版本 public class AtomicInteger extends Number implements java.io.Serializable { //需要修改的值v public volatile int v; / methodTwo:thread-2 will leave:thread-2 从控制台输出可以看出,methodOne进入了methodTwo,说明synchronized是可重入锁; 然后thread1 read(); break; case 2: write(var2); break
= new AtomicInteger(); // 需要保证多个线程使用的是同一个AtomicInteger atomicInteger.incrementAndGet(); //执行自增1 2. 无锁 vs 偏向锁 vs 轻量级锁 vs 重量级锁 这四种锁都是描述 Synchronized 关键字的状态 Synchronized 实现Thread同步的原理: 使用 Java Object Header (acquires) > MAX_COUNT) // 如果写入锁的数量大于最大数(65535,2的16次方-1)就抛出一个Error。 读写锁 (ReadWriteLock) 为了提高性能,Java提出ReadWriteLock,在读的地方用 Read Lock, 在写的地方用 Write Lock ReadWriteLock 的 关系为 Semaphore 可以完成ReentrantLock所有工作,通过 acquire() 和 release() 进行获取和释放; 2.
java的”锁“事(2) 一、公平锁和非公平锁 1.1 从ReentrantLock卖票编码演示公平和非公平现象 class Ticket { private int number = 30; 所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将 其计数器加1。 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。 当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。 2.2.2 显式锁 (即Lock)也有ReentrantLock这样的可重入锁。
java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。 1.如果线程状态切换是一个高频操作时,这将会消耗很多CPU处理时间; 2.如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间比用户代码执行的时间还要长,这种同步策略显然非常糟糕的。 synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋锁 偏向锁 Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。
java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入 值是否一样,一样则更新,否则失败。 2.悲观锁 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人 会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到, 才会转换为悲观锁,如 RetreenLock。 Synchronized 作用范围 1.作用于方法时,锁住的是对象的实例(this); 2.当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带PermGen (jdk1.8 Synchronized 核心组件 1) Wait Set:哪些调用 wait 方法被阻塞的线程被放置在这里; 2) Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中;
悲观锁 & 乐观锁悲观锁:认为多个线程访问同一个共享变量冲突的概率较大,会在每次访问共享变量之前都去真正加锁。Java中的 synchronized 锁 和 ReentrantLock就是悲观锁。 Java中的CAS就是乐观锁,比较当前值(主内存中的值)与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。 读写锁Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写锁。 自旋锁如果获取锁失败,立即再尝试获取锁,无限循环,直到获取到锁为止。第一次获取锁失败,第二次的尝试会在极短的时间内到来,一旦锁被其他线程释放,就能第一时间获取到锁。 同步锁同步锁表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。在java中 synchronized 锁就是同步锁。
可重入锁递归锁,同一个线程,外层函数获得锁,内层的也获得锁。 ...2 读取.....4 读取.....2 finished 读取.....0 finished 读取.....1 finished 读取.....3 finished 读取.....4 finished finally { lock.unlock(); } }}多线程的判断用while if只适合两个线程的判断synchronized和lock的区别参考文章存在层次上synchronized: Java 的关键字,在jvm层面上Lock: 是一个接口锁的释放synchronized: 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁Lock: 在finally中必须释放锁 = 2) { c2.await(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread
Java中导致饥饿的原因: 高优先级线程吞噬所有的低优先级线程的CPU时间。 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。 2、乐观锁和悲观锁的理解及如何实现,有哪些实现方式? 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。 在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。
一、 Java锁 1.常见的锁有synchronized和Lock() ①synchronized 是jvm层面实现的,可以直接用,不过要锁住某个对象;lock是属于j.u.c包下的接口,用的时候要实现 @lock锁与synchronized相比,lock锁添加一些其他特性,如中断锁等候和定时锁等候。 2.悲观锁与乐观锁 ①悲观锁认为世界是悲观的,当去拿数据的时候就上锁,这样别人想拿这个锁就会阻塞直到拿到锁,传统的数据库用到了这种锁,像行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。 ②乐观锁,认为一般并发是不会发生的,所以不会上锁。 Thread1开始cas(1,1,2)操作,读到了val=1;的值,线程挂起 Thread2将A的值改为2又改回来,相当于值没变,但是却被修改过。
我们都知道在 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中各种锁的优缺点的基础之一。 ,因为对象的markword和java各种类型的锁密切相关; markword数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,它的最后2bit是锁状态标志位,用来标记当前对象的状态 偏向锁 Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁 2. 这个缓存行中任何一个变量发生了变化,当cup-2需要读取这个缓存行时,就需要先将cup-1中被改变了的整个缓存行更新回主存(即使其它变量没有更改),然后cup-2才能够读取,而cup-2可能需要更改这个缓存行的变量与
由于具体业务场景的需求,需要保证数据在分布式环境下的正确更新,所以研究了一下Java中分布式锁的实现。 Java分布式锁的实现方式主要有以下三种: 数据库实现的乐观锁 Redis实现的分布式锁 Zookeeper实现的分布式锁 其中,较常用的是前两种方式,但是数据库实现方式需要较多的数据库操作,所以最终选择的是用 第一,Redis锁需要有一个超时时间,这样即便某个持有锁的节点挂了,也不到导致其他节点死锁,保证每个锁有一个UniqueId;第二,每个锁需要有一个UniqueId,确保当一个线程执行完一个任务去释放锁的时候释放的一定是自己的锁 ,否则可能存在一种场景,就是一个线程释放锁的时候,它的锁可能已经超时被释放了,而因为缺少一个UniqueId,它却释放了另一个线程的锁 基于以上两点的考虑,分别设计了获取锁和释放锁的api。 若做check的时候,检查得到确实是这个锁的UniqueId,但是在执行del方法之前,这个锁已经超时,然后新的线程也已经获取到锁了,那么del删掉的锁,便不是自己的锁,而是下一个线程的锁。
参考链接: 了解Java中的类和对象 前言 之前看到过一篇文章,写的就是Java关键字synchronized的类锁和对象锁,今天想重温一下无奈发现文章已经搜索不到,百度之大部分都是重复的那么几篇文章 Java的 synchronized 锁的是对象,也只锁对象: 对象锁是基于对堆内存内对象的头部加锁信息; 类锁是基于对类对应的 java.lang.Class对象加锁信息; 特别的, synchronized Sync.staticA() 和 Sync.staticB(); A2: A、B、C 解析:类锁与对象锁作用粒度不一,互不影响。对象锁与类静态方法之间无锁冲突。类锁与对象方法也没有锁冲突。 x的对象锁,因此 foo2() 与x里面的三个使用了对象锁的方法都是互斥的。 想获得一个Java的对象,则需要先获得Java的一个类,这便是Java的类加载。
最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 2.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。 2.非公平锁 上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式。 非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。 独享锁 VS 共享锁 1.独享锁 是指该锁一次只能被一个线程所持有。 2.共享锁 是指该锁可被多个线程所持有。 3.比较 对于Java ReentrantLock而言,其是独享锁。 Java线程锁 详细可以参考:高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景 本文标题:最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁 转载请保留页面地址:http
image.png 乐观锁 VS 悲观锁 对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。 Java中,synchronized关键字和Lock的实现类都是悲观锁。 而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。 乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
在Java环境中,synchronized 和ReentrantLock都是可重入锁。 读写锁:ReadWriteLock 在Java中通过Lock接口及对象可以方便地为对象加锁和释放锁,但是这种锁不区分读写,叫做普通锁。为提高性能,Java提供了读写锁。 在Java中,通过读写锁的接口 java.util.concurrent.locks.ReadWriteLock 的实现类 ReentrantReadWriteLock来完成对读写锁的定义与使用。 Java并发包提供的加锁模式分为独占锁与共享锁。 随着锁竞争越来越激烈,锁可能从偏向锁升级到轻量级锁,再升级到重量级锁。但在Java中锁只单向升级,不会降级。
今天我们来聊下线程中的悲观锁和乐观锁,首先提到"悲观锁","乐观锁"提到这两个名词,大家可能会先想到数据库。注意啦,我们这里讲的是多线程中的锁,而不是数据库中的锁(没听过的童鞋,可以百度了解下。 大概思想同线程中的悲乐锁思想差不多)。在Java中,常用Api提供的锁就是synchronized和lock,以及CAS。不知道大家有没有这样的疑惑,我什么场景下用哪把锁最为合适。 ? 这里最典型的是java.util.concurrent并发包中的递增操作就通过CAS自旋实现的。 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。不过从jdk1.8之后java已经对synchronized做了优化,性能上有了大幅度的提升。 ABA问题的解决思路就是在变量前面添加版本号,每次变量更新的时候都把版本号加一,这样变化过程就从“A-B-A”变成了“1A-2B-3A”。
Java中的分很多种类,按照场景的不同、特性的不同等分为了很多类,下面就来讲讲Java中锁的概念: 自旋锁:是指当一个线程在获取锁的时候,该锁已经被其他线程占用,则该线程会循环等待,并会不断检查是否成功获取锁 Java中悲观锁和乐观锁的实现 在Java语言中,对于悲观锁和乐观锁有不同的实现。 synchronized关键字和Lock相关实现类都是悲观锁。 只要一个线程获取到了对象的锁,会修改对象头中的Mark Word状态,同时线程中也会保存对应的状态。 而Java中的乐观锁最常采用的是CAS算法。 悲观锁和乐观锁的应用场景 悲观锁适用于写多读少的场景,操作资源的时候先加锁可以保证资源的正确性 乐观锁适用于读多写少的场景, 不加锁可以让读取数据的效率大幅增强 自旋锁和适应性自旋锁 阻塞或唤醒一个Java 无锁、偏向锁、轻量级锁和重量级锁 这四种锁是针对synchronized关键字提出的,在说这四种锁之前先来简单介绍一个重要的知识点:Mark Word Mark Word是保存在Java对象头中的数据,