探索JAVA并发 - 可重入锁和不可重入锁
本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
锁的简单应用
用lock来保证原子性(this.count++这段代码称为临界区)
什么是原子性,就是不可分,从头执行到尾,不能被其他线程同时执行。
可通过CAS来实现原子操作
CAS(Compare and Swap):
CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。
CAS主要通过compareAndSwapXXX()方法来实现,而这个方法的实现需要涉及底层的unsafe类
unsafe类:java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作
public class Counter{
private Lock lock = new Lock();
private int count = ;
public int inc(){
lock.lock();
this.count++;
lock.unlock();
return count;
}
}
不可重入锁
先来设计一种锁
public class Lock{
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException{
while(isLocked){
wait();
}
isLocked = true;
}
public synchronized void unlock(){
isLocked = false;
notify();
}
}
这其实是个不可重入锁,举个例子
public class Count{
Lock lock = new Lock();
public void print(){
lock.lock();
doAdd();
lock.unlock();
}
public void doAdd(){
lock.lock();
//do something
lock.unlock();
}
}
当调用print()方法时,获得了锁,这时就无法再调用doAdd()方法,这时必须先释放锁才能调用,所以称这种锁为不可重入锁,也叫自旋锁。
可重入锁
设计如下:
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = ;
public synchronized void lock()
throws InterruptedException{
Thread thread = Thread.currentThread();
while(isLocked && lockedBy != thread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = thread;
}
public synchronized void unlock(){
if(Thread.currentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == ){
isLocked = false;
notify();
}
}
}
}
相对来说,可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。
第一个线程执行print()方法,得到了锁,使lockedBy等于当前线程,也就是说,执行的这个方法的线程获得了这个锁,执行add()方法时,同样要先获得锁,因不满足while循环的条件,也就是不等待,继续进行,将此时的lockedCount变量,也就是当前获得锁的数量加一,当释放了所有的锁,才执行notify()。
如果在执行这个方法时,有第二个线程想要执行这个方法,因为lockedBy不等于第二个线程,导致这个线程进入了循环,也就是等待,不断执行wait()方法。只有当第一个线程释放了所有的锁,执行了notify()方法,第二个线程才得以跳出循环,继续执行。
这就是可重入锁的特点。
java中常用的可重入锁
synchronized
java.util.concurrent.locks.ReentrantLock
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
- AtomicLongFieldUpdater:原子更新长整型字段的更新器
- AtomicStampedReference:原子更新带有版本号的引用类型。该类将整型数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。
- AtomicReference :原子更新引用类型
- AtomicReferenceFieldUpdater :原子更新引用类型里的字段
- AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和应用类型
- AtomicIntegerArray :原子更新整型数组里的元素
- AtomicLongArray :原子更新长整型数组里的元素
- AtomicReferenceArray : 原子更新引用类型数组的元素
- AtomicBooleanArray :原子更新布尔类型数组的元素
- AtomicBoolean :原子更新布尔类型
- AtomicInteger: 原子更新整型
- AtomicLong: 原子更新长整型
探索JAVA并发 - 可重入锁和不可重入锁的更多相关文章
- Java并发编程原理与实战四十二:锁与volatile的内存语义
锁与volatile的内存语义 1.锁的内存语义 2.volatile内存语义 3.synchronized内存语义 4.Lock与synchronized的区别 5.ReentrantLock源码实 ...
- [Java并发] AQS抽象队列同步器源码解析--独占锁释放过程
[Java并发] AQS抽象队列同步器源码解析--独占锁获取过程 上一篇已经讲解了AQS独占锁的获取过程,接下来就是对AQS独占锁的释放过程进行详细的分析说明,废话不多说,直接进入正文... 锁释放入 ...
- 转:【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17199201 在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访 ...
- Java并发编程(九)-- 进程饥饿和公平锁
上一章已经提到“如果一个进程被多次回滚,迟迟不能占用必需的系统资源,可能会导致进程饥饿”,本文我们详细的介绍一下“饥饿”和“公平”. Java中导致饥饿的原因 在Java中,下面三个常见的原因会导致线 ...
- 【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确 ...
- java并发编程(四)----(JUC)Lock锁初探
首先我们来回忆一下上一节讲过的synchronized关键字,该关键字用于给代码段或方法加锁,使得某一时刻它修饰的方法或代码段只能被一个线程访问.那么试想,当我们遇到这样的情况:当synchroniz ...
- 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)
一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...
- Java并发编程-线程可见性&线程封闭&指令重排序
一.指令重排序 例子如下: public class Visibility1 { public static boolean ready; public static int number; } pu ...
- 探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll
> sleep/wait/notify/notifyAll分别有什么作用?它们的区别是什么?wait时为什么要放在循环里而不能直接用if? ## 简介 首先对几个相关的方法做个简单解释,Obje ...
随机推荐
- 【2018寒假集训Day 7】【最短路径】三种算法的模板
Luogu单源最短路径模版题 dijkstra #include<cstdio> #include<vector> using namespace std; const int ...
- JavaScript笔记十二
1.DOM对CSS的操作 - 读取和修改内联样式 - 使用style属性来操作元素的内联样式 - 读取内联样式: 语法:元素.style.样式名 - 例子: 元素.style.width 元素.sty ...
- Go语言冒泡、选择、插入、快速排序实战浅析
Hello,各位小伙伴大家好,我是小栈君,今天为大家带来的分享是关于go语言中的排序实战浅析. 我们就实际操作关于go的冒泡排序.选择排序.插入排序和快速排序四种方式的理论和实战进行分享,希望能够为大 ...
- MySQL双主+keepalived实现高可用实现(热备)
环境:centos6.7 最小化安装 192.168.100.152 master 主192.168.100.153 slave 从192.168.100.132 v_ip 浮动IP 配置ssh密码登 ...
- JavaScript---1.计算机的编程基础
学习内容:编程语言.计算机基础 1编程语言 程序员通过编程语言来控制计算机 编程语言:机器语言(计算机只认识机器语言).汇编语言(直接对硬件操作,指令采用英文缩写的标识符,容易记忆).高级语言(C\C ...
- react新版本配置代理
新学习react 开始配置react跨域的时候 在网上查看到是在packjson.json里面添加如下代码: "proxy": { "/api": { &quo ...
- window.open()打开新窗口 及参数
在jsp页面中需要使用到弹出窗口,想到js的window对象有一个open方法可以弹出窗口,于是对open方法进行记录. 首先是open方法的语法及定义: 定义: open() 方法用于打开一个新的浏 ...
- split分割(拆分)文件
split分割(拆分)文件 需求:指定文件大小拆分文件 # ll -h test/ |grep vmcore -rw-r--r-- 1 root root 12G 12月 7 00:20 vmco ...
- win10 安装 MySQL-5.7.28 记录
目录 一.安装前准备 二.安装步骤 三.安装时踩的坑 一.安装前准备 1.云盘下载安装包以及客户端工具 下载地址:MySQL-5.7.28 + SQLyog 2.官网下载安装包 下载地址:https: ...
- luogu P2824 [HEOI2016/TJOI2016]排序
题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行 ...