首页 雷火电竞官网正文

张岩,Java锁-重入锁,读写锁,达观锁,失望锁,CAS无锁形式-雷火电竞平台官网

admin 雷火电竞官网 2019-10-28 120 0

重入锁

锁作为并发同享数据,确保一致性的东西,在JAVA渠道有多种完结(如 synchronized(重量级) 和 ReentrantLock(轻量级)等等 ) 。这些现已写好供给的锁为咱们开发供给了便当。

重入锁,也叫做递归锁,指的是同一线程 外层函数取得锁之后 ,内层递归函数依然有获取该锁的代码,但不受影响。

在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁

public class Test implements Runnable {
public synchronized void get() {
System.out.println("name:" + Thread.currentThread().getName() + " get();");
set();
}
public synchronized void set() {
System.out.println("name:" + Thread.currentThread().getName() + " set();");
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
Test ss = new Test();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
}
}
public class Test02 extends Thread {
ReentrantLock lock = new ReentrantLock();
public void get() {
lock.lock();
System.out.println(Thread.currentThread().getId());
set();
lock.unlock();
}
public void set() {
lock.lock();
System.out.println(Thread.currentThread().getId());
lock.unlock();
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
Test ss = new Test();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
}
}

读写锁

比较Java中的锁(Locks in Java)里Lock完结,读写锁更杂乱一些。假定你的程序中涉及到对一些同享资源的读和写操作,且写操作没有读操作那么频频。在没有写操作的时分,两个线程一起读一个资源没有任何问题,所以应该答应多个线程能在一起读取同享资源。可是假如有一个线程想去写这些同享资源,就不应该再有其它线程对该资源进行读或写(译者注:也便是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需求一个读/写锁来处理这个问题。Java5在java.util.concurrent包中现已包括了读写锁。尽管如此,咱们仍是应该了解其完结背面的原理。

public class Cache {
static Map map = new HashMap();
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock r = rwl.readLock();
static Lock w = rwl.writeLock();
// 获取一个key对应的value
public static final Object get(String key) {
r.lock();
try {
System.out.println("正在做读的操作,key:" + key + " 开端");
Thread.sleep(100);
Object object = map.get(key);
System.out.println("正在做读的操作,key:" + key + " 完毕");
System.out.println();
return object;
} catch (InterruptedException e) {
} finally {
r.unlock();
}
return key;
}
// 设置key对应的value,并回来旧有的value
public static final Object put(String key, Object value) {
w.lock();
try {
System.out.println("正在做写的操作,key:" + key + ",value:" + value + "开端.");
Thread.sleep(100);
Object object = map.put(key, value);
System.out.println("正在做写的操作,key:" + key + ",value:" + value + "完毕.");
System.out.println();
return object;
} catch (InterruptedException e) {
} finally {
w.unlock();
}
return value;
}
// 清空一切的内容
public static final void clear() {
w.lock();
try {
map.clear();
} finally {
w.unlock();
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Cache.put(i + "", i + "");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Cache.get(i + "");
}
}
}).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,涉及到三个操作数,数据地点的内存值,预期值,新值。当需求更新时,判别当时内存值与之前取到的值是否持平,若持平,则用新值更新,若失利则重试,一般状况下是一个自旋操作,即不断的重试。

失望锁

总是假定最坏的状况,每次取数据时都以为其他线程会修正,所以都会加锁(读锁、写锁、行锁等),当其他线程想要拜访数据时,都需求堵塞挂起。能够依托数据库完结,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思维也是失望锁。

原子类

java.util.concurrent.atomic包:原子类的小东西包,支撑在单个变量上免除锁的线程安全编程

原子变量类相当于一种泛化的 volatile 变量,能够支撑原子的和有条件的读-改-写操作。AtomicInteger 表明一个int类型的值,并供给了 get 和 set 办法,这些 Volatile 类型的int变量在读取和写入上有着相同的内存语义。它还供给了一个原子的 compareAndSet 办法(假如该办法成功履行,那么将完结与读取/写入一个 volatile 变量相同的内存作用),以及原子的增加、递加和递减等办法。AtomicInteger 表面上十分像一个扩展的 Counter 类,但在发作竞赛的状况下能供给更高的可伸缩性,由于它直接利用了硬件对并发的支撑。

为什么会有原子类

CAS:Compare and Swap,即比较再交流。

jdk5增加了并发包java.util.concurrent.*,其下面的类运用CAS算法完结了差异于synchronouse同步锁的一种达观锁。JDK 5之前Java言语是靠synchronized关键字确保同步的,这是一种独占锁,也是是失望锁。

假如同一个变量要被多个线程拜访,则能够运用该包中的类

AtomicBoolean

AtomicInteger

AtomicLong

AtomicReference

CAS无锁形式

什么是CAS

CAS:Compare and Swap,即比较再交流。

jdk5增加了并发包java.util.concurrent.*,其下面的类运用CAS算法完结了差异于synchronouse同步锁的一种达观锁。JDK 5之前Java言语是靠synchronized关键字确保同步的,这是一种独占锁,也是是失望锁。

CAS算法了解

(1)与锁比较,运用比较交流(下文简称CAS)会使程序看起来愈加杂乱一些。但由于其非堵塞性,它对死锁问题天然生成免疫,而且,线程间的相互影响也远远比根据锁的办法要小。更为重要的是,运用无锁的办法彻底没有锁竞赛带来的体系开支,也没有线程间频频调度带来的开支,因而,它要比根据锁的办法具有更优越的功能。

(2)无锁的优点:

榜首,在高并发的状况下,它比有锁的程序具有更好的功能;

第二,它天然生成便是死锁免疫的。

就凭仗这两个优势,就值得咱们冒险测验运用无锁的并发。

(3)CAS算法的进程是这样:它包括三个参数CAS(V,E,N): V表明要更新的变量,E表明预期值,N表明新值。仅当V值等于E值时,才会将V的值设为N,假如V值和E值不同,则阐明现已有其他线程做了更新,则当时线程什么都不做。最终,CAS回来当时V的实在值。

(4)CAS操作是抱着达观的情绪进行的,它总是以为自己能够成功完结操作。当多个线程一起运用CAS操作一个变量时,只要一个会胜出,并成功更新,其他均会失利。失利的线程不会被挂起,仅是被奉告失利,而且答应再次测验,当然也答应失利的线程抛弃操作。根据这样的原理,CAS操作即便没有锁,也能够发现其他线程对当时线程的搅扰,并进行恰当的处理。

(5)简略地说,CAS需求你额定给出一个期望值,也便是你以为这个变量现在应该是什么姿态的。假如变量不是你幻想的那样,那阐明它现已被他人修正过了。你就从头读取,再次测验修正就好了。

(6)在硬件层面,大部分的现代处理器都现已支撑原子化的CAS指令。在JDK 5.0今后,虚拟机便能够运用这个指令来完结并发操作和并发数据结构,而且,这种操作在虚拟机中能够说是无处不在。

常用原子类

Java中的原子操作类大致能够分为4类:原子更新根本类型、原子更新数组类型、原子更新引证类型、原子更新特点类型。这些原子类中都是用了无锁的概念,有的当地直接运用CAS操作的线程安全的类型。

AtomicBoolean

AtomicInteger

AtomicLong

AtomicReference

public class Test0001 implements Runnable {
private static Integer count = 1;
private static AtomicInteger atomic = new AtomicInteger();
@Override
public void run() {
while (true) {
int count = getCountAtomic();
System.out.println(count);
if (count >= 150) {
break;
}
}
}
public synchronized Integer getCount() {
try {
Thread.sleep(50);
} catch (Exception e) {
// TODO: handle exception
}
return count++;
}
public Integer getCountAtomic() {
try {
Thread.sleep(50);
} catch (Exception e) {
// TODO: handle exception
}
return atomic.incrementAndGet();
}
public static void main(String[] args) {
Test0001 test0001 = new Test0001();
Thread t1 = new Thread(test0001);
Thread t2 = new Thread(test0001);
t1.start();
t2.start();
}
}

CAS(达观锁算法)的根本假定条件

CAS比较与交流的伪代码能够表明为:

do{

备份旧数据;

根据旧数据结构新数据;

}while(!CAS( 内存地址,备份的旧数据,新数据 )

(上图的解说:CPU去更新一个值,但假如想改的值不再是原本的值,操作就失利,由于很明显,有其它操作先改变了这个值。)

便是指当两者进行比较时,假如持平,则证明同享数据没有被修正,替换成新值,然后持续往下运转;假如不持平,阐明同享数据现已被修正,抛弃现已所做的操作,然后从头履行方才的操作。简单看出 CAS 操作是根据同享数据不会被修正的假定,采用了类似于数据库的 commit-retry 的形式。当同步抵触呈现的时机很少时,这种假定能带来较大的功能提高。

public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
//获取当时值
int current = get();
//设置期望值
int next = current + 1;
//调用Native办法compareAndSet,履行CAS操作
if (compareAndSet(current, next))
//成功后才会回来期望值,不然无线循环
return next;
}
}

CAS缺陷

CAS存在一个很明显的问题,即ABA问题。

问题:假如变量V初度读取的时分是A,而且在预备赋值的时分检查到它依然是A,那能阐明它的值没有被其他线程修正过了吗?

假如在这段期间从前被改成B,然后又改回A,那CAS操作就会误以为它从来没有被修正过。针对这种状况,java并发包中供给了一个带有符号的原子引证类AtomicStampedReference,它能够经过操控变量值的版原本确保CAS的正确性。

分布式锁

假如想在不同的jvm中确保数据同步,运用分布式锁技能。

有数据库完结、缓存完结、Zookeeper分布式锁

雷火电竞版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

最近发表

    雷火电竞平台官网_雷火竞猜_雷火电竞csgo

    http://www.koisoku.net/

    |

    Powered By

    使用手机软件扫描微信二维码

    关注我们可获取更多热点资讯

    雷火电竞出品