Java锁

  锁一般来说用作资源控制,限制资源访问,防止在并发环境下造成数据错误

  锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized(重量级) 和 ReentrantLock(轻量级)等等 ) 。这些已经写好提供的锁为我们开发提供了便利。

一、重入锁

  重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
  在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁。

  synchronized和ReentrantLock就是重入锁对应的实现

  synchronized重量级的锁

  ReentrantLock轻量级的锁   lock()代表加入锁    unlock()代表释放锁

1、不可重入锁

  说明当没有释放该锁时。其他线程获取该锁会进行等待

MyLock:

package com.zn.lockTest;

public class MyLock {
//标识锁是否可用 如果值为true代表当前有线程正在使用该锁,如果为false代表没有人用锁
private boolean isLocked=false;
//获取锁:加锁
public synchronized void lock() throws InterruptedException {
//判断当前该锁是否正在使用
while (isLocked){
wait();
}
//当前没有人使用情况下就占用该锁
isLocked=true;
}
//释放锁
public synchronized void unLock(){
//将当前锁资源释放
isLocked=false;
//唤起正在等待使用锁的线程
notify();
}
}

MyLockTest:

package com.zn.lockTest;

public class MyLockTest {
MyLock myLock=new MyLock();
//A业务方法
public void print() throws InterruptedException {
//获取一把锁
myLock.lock();
System.out.println("print业务方法");
doAdd();
//释放锁
myLock.unLock();
} //B业务方法
public void doAdd() throws InterruptedException {
//获取一把锁
myLock.lock();
System.out.println("doAdd业务方法");
//释放锁
myLock.unLock();
} public static void main(String[] args) throws InterruptedException {
MyLockTest test=new MyLockTest();
test.print();
}
}

控制台效果:

  

2、synchronized可重入性

  如果当前A持有一把锁,在A业务内部调用B,那么B也同样拥有这把锁的使用权限

MyLock:

package com.zn.lockTest;

public class MyLock {
//标识锁是否可用 如果值为true代表当前有线程正在使用该锁,如果为false代表没有人用锁
private boolean isLocked=false;
//获取锁:加锁
public synchronized void lock() throws InterruptedException {
//判断当前该锁是否正在使用
while (isLocked){
wait();
}
//当前没有人使用情况下就占用该锁
isLocked=true;
}
//释放锁
public synchronized void unLock(){
//将当前锁资源释放
isLocked=false;
//唤起正在等待使用锁的线程
notify();
}
}

MyLockTest:

package com.zn.lockTest;

public class MyLockTest {
//A业务方法
public synchronized void print() throws InterruptedException {
//获取了一把锁
System.out.println("print业务方法");
doAdd();
} //B业务方法
public synchronized void doAdd() throws InterruptedException {
System.out.println("doAdd业务方法");
//释放锁
} public static void main(String[] args) throws InterruptedException {
MyLockTest test=new MyLockTest();
test.print();
}
}

控制台效果:

  

3、ReentrantLock

  同样具有可重入性

package com.zn.lockTest;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class MyLockTest { //ReentrantLock
//创建锁对象
Lock lock=new ReentrantLock();
//A业务方法
public void print() throws InterruptedException {
//获取了一把锁
lock.lock();
System.out.println("print业务方法");
doAdd();
//释放锁
lock.unlock();
} //B业务方法
public void doAdd() throws InterruptedException {
//获取了一把锁
lock.lock();
System.out.println("doAdd业务方法");
//释放锁
lock.unlock();
} public static void main(String[] args) throws InterruptedException {
MyLockTest test=new MyLockTest();
test.print();
}
}

控制台效果:

  

4、ReentrantLock底层

    /**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
//默认非公平锁
sync = new NonfairSync();
} /**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
//如果为true代表公平锁,否则为非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}

MyReentrantLock:

package com.zn.lockTest;

public class MyReentrantLock {
//标识锁是否可用 如果值为true代表当前有线程正在使用该锁,如果为false代表没有人用锁
private boolean isLocked=false;
//当前线程
Thread lockedBy=null;
//加锁数量计数
Integer lockedCount=0;
//加锁
public synchronized void lock() throws InterruptedException {
//获取当前线程
Thread thread=Thread.currentThread();
//判断当前是否正在使用锁,如果正在使用则对比当前使用要使用锁的线程和之前使用锁的线程是否一致
//如果一致代表可以重入,继续使用锁,不会发生阻塞
//如果不一致代表当前不是一个线程,则等待
while (isLocked && thread!=lockedBy){
wait();
}
//占用锁
isLocked=true;
//计数+1
lockedCount++;
//赋值线程
lockedBy=thread;
}
//释放锁
public synchronized void unlock(){
//判断当前是否是用一个线程
if(Thread.currentThread()==this.lockedBy){
//锁使用计数器-1
lockedCount--;
//判断计数器是否为0,如果为0则释放锁,然后唤醒正在等待的线程
if(lockedCount==0){
isLocked=false;
notify();
}
}
}
}

二、读写锁

  相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。

  假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。

  Java5在java.util.concurrent包中已经包含了读写锁。

package com.zn.lockTest;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLock {
//创建一个集合
static Map<String,String> map=new HashMap<String,String>();
//创建一个读写锁
static ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
//获取读锁
static Lock readLock=lock.readLock();
//获取写锁
static Lock writeLock=lock.writeLock();
//写操作
public Object put(String key,String value){
writeLock.lock();
try {
System.out.println("Write正在执行写操作~");
Thread.sleep(100);
String put = map.put(key, value);
System.out.println("Write写操作执行完毕~");
return put;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writeLock.unlock();
}
return null; } //写操作
public Object get(String key){
readLock.lock();
try {
System.out.println("Read正在执行读操作~");
Thread.sleep(100);
String value = map.get(key);
System.out.println("Read读操作执行完毕~");
return value;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readLock.unlock();
}
return null; } public static void main(String[] args) {
ReadWriteLock lock=new ReadWriteLock();
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(()->{
try {
//写操作
lock.put(finalI +"","value"+finalI);
//读操作
System.out.println(lock.get(finalI+""));
} catch (Exception e) {
e.printStackTrace();
}
}).start();
} }
}

控制台效果:

  

三、乐观锁

  总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。

  version方式:

    一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

  核心SQL语句:

    update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

  CAS操作方式:

    即compare and swap 或者 compare and set,涉及到三个操作数(V、E、N),数据所在的内存值(V),预期值(E),新值(N)。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

四、悲观锁

  总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。

Java锁的深度化--重入锁、读写锁、乐观锁、悲观锁的更多相关文章

  1. 【Java并发工具类】StampedLock:比读写锁更快的锁

    前言 ReadWriteLock适用于读多写少的场景,允许多个线程同时读取共享变量.但在读多写少的场景中,还有更快的技术方案.在Java 1.8中, 提供了StampedLock锁,它的性能就比读写锁 ...

  2. ReentrantReadWriteLock 可重入的读写锁

    可重入:就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候会释放一次锁,直到该线程加锁次数为0,这个线程才释放锁. 读写锁: 也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能 ...

  3. Java并发-显式锁篇【可重入锁+读写锁】

    作者:汤圆 个人博客:javalover.cc 前言 在前面并发的开篇,我们介绍过内置锁synchronized: 这节我们再介绍下显式锁Lock 显式锁包括:可重入锁ReentrantLock.读写 ...

  4. java并发包&线程池原理分析&锁的深度化

          java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...

  5. JAVA多线程(三) 线程池和锁的深度化

    github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...

  6. 25.Java锁的深度化

    Java锁的深度化 悲观锁.乐观锁.排他锁 场景 当多个请求同时操作数据库时,首先将订单状态改为已支付,在金额加上200,在同时并发场景查询条件下,会造成重复通知. SQL: Update 悲观锁与乐 ...

  7. Java多线程之ReentrantLock重入锁简介与使用教程

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html  我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoize ...

  8. 【学习】005 线程池原理分析&锁的深度化

    线程池 什么是线程池 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序 都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 第一:降低资源消耗.通过重复 ...

  9. 23、Java并发性和多线程-重入锁死

    以下内容转自http://ifeve.com/reentrance-lockout/: 重入锁死与死锁和嵌套管程锁死非常相似.锁和读写锁两篇文章中都有涉及到重入锁死的问题. 当一个线程重新获取锁,读写 ...

随机推荐

  1. unicode编码与解码

    unicode编码与解码,代码如下 package com.fenqiguanjia.api.services; /** * Created by daixianjun on 2017/9/3. */ ...

  2. JAVA9中文API百度网盘免费下载

    JAVA9中文API百度网盘免费下载: https://pan.baidu.com/s/1tvHYQA8yyAS4xUFxwWrx_Q 提取码: 6e5h

  3. js编写trim()函数

    1.js中本身是没有trim函数的,不过你可以自己写一个 function trim(str) {     var newStr = str.replace(/^\s*$/g,'')     retr ...

  4. iOS 编程:NSURLSession

    定义:描述 Foundation 框架类在标准的网络传输协议下,用 URLs 连接因特网并与服务器交互的一整套体系. 支持的传输协议: File Transfer Protocol (ftp://) ...

  5. webpack中打包拷贝静态文件CopyWebpackPlugin插件

    copyWebpackPlugin: 作用:用于webpack打包时拷贝文件的插件包 安装:npm install copyWebpackPlugin@版本号 使用:// copy custom st ...

  6. PAT-进制转换-B1022 D进制的A+B (20分)

    题目描述: 输入两个非负10进制整数A和B(≤230-1),输出A+B的D(1<D≤10)进制数. 输入格式: 输入在一行中依次给出3个整数A.B和D. 样例: 输入:123 456 8 输出: ...

  7. 得亏了它,我才把潜藏那么深的Bug挖出来

    2020年写了很多事故解决的文章,并不是我绞尽脑汁想出来的,而是真的遇到了这些问题.通过文章的方式记录下来,分享出去,才有意义. 事故背景 首先看下面的图吧,这是我从cat上截的图. 可以看到是一个R ...

  8. SpringBoot&Shiro实现权限管理

    SpringBoot&Shiro实现权限管理 引言 相信大家前来看这篇文章的时候,是有SpringBoot和Shiro基础的,所以本文只介绍整合的步骤,如果哪里写的不好,恳请大家能指出错误,谢 ...

  9. sql--自链接(推荐人)

    表1: 需求:查出推荐人,和被推荐人 1.通过group_concat函数和分组,查出每个id推荐的人有哪些 select group_concat(u_name, u_id) as referce_ ...

  10. MySql5.7.28下载、安装、登陆详解

    进入MySql官网下载,页面如下 根据自己需求,选择适合自己的进行下载 然后解压安装包到自己的喜欢的路径 配置环境变量 添加环境系统变量 MYSQL_HOME:D:\Program Files\mys ...