- 浏览: 154026 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
lyaqys:
lz实现的OptimisticExclusiveLock有点问 ...
java park/unpark 【java并发】基于JUC CAS原理,自己实现简单独占锁
LockSupport.park(); 停止
System.out.println("======");
为阻塞线程提供基础的功能,它由一对park和unpark组成,park会阻塞当前线程,unpark“唤醒”等待线程;内部使用了类似信号量的“许可”机制,该许可为0,park会在许可等于0的时候下阻塞,等于1的时候立即返回,并且将许可减为0,umpark会尝试唤醒线程,并且将许可+1(最大值就是1)。因此,如果先调用unpark方法,再调用park是无效的
unpark /patk 成对出现
synchronized的基本原理回顾
在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1.
synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。
JUC CAS乐观锁基本原理
synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁就是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。
原理有点类似于在数据库记录字段增加一个版本号,每次更新的时候做两个事情:
1.检查数据库记录当前的版本号和读取到的版本号是否一致,不一致说明数据已不是最新,更新失败需要重试.
2.如果版本号一致,更新成功,同时将版本号加1.
在jdk1.5开始,Doug Lea在JUC类库里提供了类似的乐观锁的机制叫CAS.
CAS简介:compareAndSet的意思,就是先比较是否是期望值(是期望值,说明没人更改过,当然也有可能有ABA情况),如果是再设值,不是就设值失败,线程阻塞。如果是基于这个,就要保证比较和设值这两个动作是原子性的,如何保证呢?这个是借助于JNI,利用CPU硬件支持来完成的。利用硬件提供swap和test_and_set指令,单CPU下同一指令的多个指令周期不可中断,SMP中通过锁总线支持这两个指令的原子性。
volilate关键字
Java语言规范允许线程保存共享成员变量的私有拷贝,当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样就延伸出可见性的一个问题。
CAS解决了比较和更新的原子性,但是还有另外一个问题就是要保证可见性。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile关键字有两层含义:
1.对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
2.volatile前后的代码不能重排
其他话题
volilate和cas只能乐观锁保证的状态控制的正确,而在设置状态失败的时候,仍然需要阻塞线程。juc里提供了LockSupport的park和unpark方法用于阻塞线程。而不同的场景下需要不同的等待策略和锁共享策略,juc提供了AbstractQueuedSynchronizer(AQS)为基类的一序列不同的锁,底层都是基于CAS、LocakSupport和Queue来管理,后续有时间细细分析。
juc基于的CAS,提供了带有原子性的基本类型封装类,如AtomicInteger、AtomicLong等。
AtomicInteger原理:
如自增:
Java代码
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) //cas
return current;
}
}
compareAndSet的实现如下:
Java代码
public final boolean compareAndSet(int expect, int update) {
urn unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用
}
自己实现简单乐观独占锁
基于cas,本人简单实现了一个乐观独占锁,代码如下:
基于CAS简单乐观独占锁
Java代码
package lock.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
/**
* 简单乐观独占锁
*/
public class OptimisticExclusiveLock {
/**
* 独占锁标记 true 锁不可用 false 锁可用
*/
private AtomicBoolean state = new AtomicBoolean(false);
List<Thread> queue = new ArrayList<Thread>();//阻塞队列
public boolean lock() {
if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行
return true; // 利用CAS
} else {
queue.add(Thread.currentThread());//加入阻塞队列
LockSupport.park();//阻塞线程
return false;
}
}
public boolean unLock() {
if (state.get()) {
queue.remove(Thread.currentThread());//从队列里移除
if (state.compareAndSet(true, false)) {// 利用CAS
if(!queue.isEmpty()){
LockSupport.unpark(queue.get(0));//唤醒第一个等待线程
}
return true;
}
return false;
} else {
return false;
}
}
}
简单乐观独占锁测试
Java代码
package lock.test;
/**
* @author Administrator 独占锁测试
*/
public class OptimisticExclusiveLockTest {
public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁
public static volatile int i = 0; // 保证可见性
public class Task implements Runnable {
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
i += 2;
System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i);
} finally {
lock.unLock();//释放锁
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public void runTask() {
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new Task(), "thread" + i);
t.start();
}
}
public static void main(String[] args) {
OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest();
test.runTask();
}
}
这里实现的简单乐观独占锁很简单,但是能保证并发性。
JUC里面基于CAS实现了很多的锁,主要是基于AQS实现,如ReentrantLock,CountDownLatch,Semaphore,FutureTask等,适用于不同的锁场景。
System.out.println("======");
为阻塞线程提供基础的功能,它由一对park和unpark组成,park会阻塞当前线程,unpark“唤醒”等待线程;内部使用了类似信号量的“许可”机制,该许可为0,park会在许可等于0的时候下阻塞,等于1的时候立即返回,并且将许可减为0,umpark会尝试唤醒线程,并且将许可+1(最大值就是1)。因此,如果先调用unpark方法,再调用park是无效的
unpark /patk 成对出现
synchronized的基本原理回顾
在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1.
synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。
JUC CAS乐观锁基本原理
synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁就是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。
原理有点类似于在数据库记录字段增加一个版本号,每次更新的时候做两个事情:
1.检查数据库记录当前的版本号和读取到的版本号是否一致,不一致说明数据已不是最新,更新失败需要重试.
2.如果版本号一致,更新成功,同时将版本号加1.
在jdk1.5开始,Doug Lea在JUC类库里提供了类似的乐观锁的机制叫CAS.
CAS简介:compareAndSet的意思,就是先比较是否是期望值(是期望值,说明没人更改过,当然也有可能有ABA情况),如果是再设值,不是就设值失败,线程阻塞。如果是基于这个,就要保证比较和设值这两个动作是原子性的,如何保证呢?这个是借助于JNI,利用CPU硬件支持来完成的。利用硬件提供swap和test_and_set指令,单CPU下同一指令的多个指令周期不可中断,SMP中通过锁总线支持这两个指令的原子性。
volilate关键字
Java语言规范允许线程保存共享成员变量的私有拷贝,当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样就延伸出可见性的一个问题。
CAS解决了比较和更新的原子性,但是还有另外一个问题就是要保证可见性。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile关键字有两层含义:
1.对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
2.volatile前后的代码不能重排
其他话题
volilate和cas只能乐观锁保证的状态控制的正确,而在设置状态失败的时候,仍然需要阻塞线程。juc里提供了LockSupport的park和unpark方法用于阻塞线程。而不同的场景下需要不同的等待策略和锁共享策略,juc提供了AbstractQueuedSynchronizer(AQS)为基类的一序列不同的锁,底层都是基于CAS、LocakSupport和Queue来管理,后续有时间细细分析。
juc基于的CAS,提供了带有原子性的基本类型封装类,如AtomicInteger、AtomicLong等。
AtomicInteger原理:
如自增:
Java代码
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) //cas
return current;
}
}
compareAndSet的实现如下:
Java代码
public final boolean compareAndSet(int expect, int update) {
urn unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用
}
自己实现简单乐观独占锁
基于cas,本人简单实现了一个乐观独占锁,代码如下:
基于CAS简单乐观独占锁
Java代码
package lock.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
/**
* 简单乐观独占锁
*/
public class OptimisticExclusiveLock {
/**
* 独占锁标记 true 锁不可用 false 锁可用
*/
private AtomicBoolean state = new AtomicBoolean(false);
List<Thread> queue = new ArrayList<Thread>();//阻塞队列
public boolean lock() {
if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行
return true; // 利用CAS
} else {
queue.add(Thread.currentThread());//加入阻塞队列
LockSupport.park();//阻塞线程
return false;
}
}
public boolean unLock() {
if (state.get()) {
queue.remove(Thread.currentThread());//从队列里移除
if (state.compareAndSet(true, false)) {// 利用CAS
if(!queue.isEmpty()){
LockSupport.unpark(queue.get(0));//唤醒第一个等待线程
}
return true;
}
return false;
} else {
return false;
}
}
}
简单乐观独占锁测试
Java代码
package lock.test;
/**
* @author Administrator 独占锁测试
*/
public class OptimisticExclusiveLockTest {
public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁
public static volatile int i = 0; // 保证可见性
public class Task implements Runnable {
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
i += 2;
System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i);
} finally {
lock.unLock();//释放锁
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public void runTask() {
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new Task(), "thread" + i);
t.start();
}
}
public static void main(String[] args) {
OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest();
test.runTask();
}
}
这里实现的简单乐观独占锁很简单,但是能保证并发性。
JUC里面基于CAS实现了很多的锁,主要是基于AQS实现,如ReentrantLock,CountDownLatch,Semaphore,FutureTask等,适用于不同的锁场景。
评论
1 楼
lyaqys
2014-12-30
lz实现的OptimisticExclusiveLock有点问题哦,
1. List<Thread> queue = new ArrayList<Thread>();//阻塞队列 不是线程安全的
2. The park method may also return at any other time, for "no reason", so in general must be invoked within a loop that rechecks conditions upon return. In this sense park serves as an optimization of a "busy wait" that does not waste as much time spinning, but must be paired with an unpark to be effective.----park可能无缘无故return,因此要用while包起来。
可以参看LockSupport源码里面给的例子
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters
= new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// Block while not first in queue or cannot acquire lock
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
1. List<Thread> queue = new ArrayList<Thread>();//阻塞队列 不是线程安全的
2. The park method may also return at any other time, for "no reason", so in general must be invoked within a loop that rechecks conditions upon return. In this sense park serves as an optimization of a "busy wait" that does not waste as much time spinning, but must be paired with an unpark to be effective.----park可能无缘无故return,因此要用while包起来。
可以参看LockSupport源码里面给的例子
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters
= new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// Block while not first in queue or cannot acquire lock
while (waiters.peek() != current ||
!locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
发表评论
-
java 栈内存解惑
2013-10-23 23:41 676int j = 0; j = j++; ... -
Google Guava Collections 使用介绍
2013-07-16 18:10 713Google Guava Collections 使用介绍 J ... -
Google Collections(Guava)中强大的Concurrent MapMaker
2013-07-16 18:07 1364仔细研究了刚发布1.0版本的Google Collection ... -
java wait 研究
2013-06-28 17:07 780[color=red]java wait 的使用必须放在实例对 ... -
java钩子函数的使用已经kill
2013-06-27 22:31 1557package com.aircoder.test; imp ... -
java获取所有的线程信息
2013-06-24 20:02 1604public class T2 { public sta ... -
java 获取mysql datetime 时间注意
2013-05-16 14:43 1522class SPubinfo implements RowMa ... -
java wait的解锁理解********
2013-04-18 10:49 947很多书都说wait会释放线程加的锁,其实经过试验不是这样的, ... -
jvm 关闭处理注册方法
2013-04-08 16:11 761import java.lang.*; public cla ... -
wget ftp 下载文件java代码
2013-04-05 15:16 1160private boolean wget(String fil ... -
xPath 解析xml
2013-04-05 15:14 739使用xPath 根据路径解析文件. xpath 具有多个版本。 ... -
java callable疑惑:
2013-04-05 15:12 585看了 线程持的源码和Futuretask的源码终于明白了 Fu ... -
ubuntu eclipse 问题
2013-04-05 03:30 795Eclipse 3.6 在 Ubuntu 10.04 下会出现 ... -
ehchahe 例子
2013-01-23 15:40 1018package test; import net.sf.eh ... -
java 类加载
2012-12-24 15:21 7601: 访问一个类的静态方法的时候。不会引起一个类的初始化,即类 ... -
java 获取图片高和宽
2012-12-13 17:01 1402public static Map<String,Int ... -
java建立socket,返回浏览器的请求
2012-12-01 01:58 1028package com.jdk.api.test; impo ... -
schedulePool.scheduleAtFixedRate 是个误解
2012-11-22 20:34 1246我们经常使用的java定时器单线程执行,例如: 一个任务每个 ... -
ExecutorCompletionService
2012-11-19 22:36 714package com.jdk.api; import ja ...
相关推荐
Java 多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池) Java 多线程是 Java 语言中的一种并发编程机制,允许程序同时执行多个线程,以提高程序的执行效率和响应速度。 Java 多线程机制提供了...
在Java并发编程中,CAS(Compare and Swap)是一种无锁算法,用于在多线程环境中实现对共享变量的原子性更新。相比于传统的锁机制,如`synchronized`和`Lock`,CAS具有更低的开销,因为它避免了线程阻塞。本文将深入...
java高级技术JUC高并发编程教程2021(1.5G) 〖课程介绍〗: java高级技术JUC高并发编程教程2021(1.5G) 〖课程目录〗: 01-JUC高并发编程-课程介绍.mp4 02-JUC高并发编程-JUC概述和进程线程概念(1).mp4 03-JUC...
Java并发工具包(J.U.C)是Java编程语言中用于并发编程的一系列工具包的统称,它包含了一系列方便实现多线程编程的类和接口,使得开发者可以更加方便地编写高效、线程安全的程序。本文将深入浅出地探讨J.U.C的原理和...
根据提供的文档内容,以下是关于Java多线程并发编程与JUC知识点的详细解读: 1. 线程的状态 Java线程在其生命周期中可以拥有不同的状态。线程可能处于以下几种状态: - 新建(New):线程被创建时的状态。 - 就绪...
JUC,全称为Java Util Concurrency,是Java并发包的简称,包含了大量用于处理并发问题的类和接口,极大地简化了多线程环境下的编程。在这个深度解析JUC线程锁框架的主题中,我们将探讨其核心组件、设计模式以及如何...
Java并发编程是Java开发中的重要领域,而Java.util.concurrent(JUC)工具包则是Java并发编程的核心组件。这个集合提供了一系列高效、线程安全的类和接口,用于简化多线程环境下的编程任务。本资源"JUC代码收集,...
JUC(java.util.concurrent)是Java提供的一个并发编程工具包,它是为了更高效地处理线程同步和线程间协作而设计的一系列类和接口。JUC从JDK 1.5版本开始被引入,并随着后续版本的更新不断丰富和完善。JUC的出现极大...
本文将深入探讨其中的关键概念,包括读写锁、可重入锁、CAS原理以及volatile关键字。 首先,我们来看读写锁。读写锁允许多个线程同时进行读操作,但在写操作时,只有一个线程能够获得锁。这种设计极大地提高了并发...
Java多线程与并发处理是Java编程中的高级话题,涉及到JUC(java.util.concurrent)包中的原子类、CAS(Compare-And-Swap)机制、Unsafe类以及多线程并发的无锁方案和线程安全的实现方法。 CAS是一种无锁的同步机制...
这个类位于java.util.concurrent.locks包下,是实现并发编程中AQS(AbstractQueuedSynchronizer)框架的重要基础之一。LockSupport的目的是为了简化锁和其他同步类的实现,允许开发者创建更加高级的并发工具。 ...
Java并发编程领域中,JUC(Java Concurrency Utility)库扮演着至关重要的角色,它提供了高效、安全的多线程编程工具。JUC包括线程、线程池、CAS(Compare and Swap)操作、volatile关键字以及其他相关的底层原理和...
Java并发包(java.util.concurrent,简称JUC)提供了一系列工具和类库来帮助开发者简化并发编程的工作。其中,AbstractQueuedSynchronizer(简称AQS)是构建各种同步器的核心组件。 AQS是一个抽象的队列同步器,它...
Java 并行程序开发的基础, 包括 Java 中 Thread 的基本使用方法等第3章介绍了 JDK 内部对并行程序开发的支持, 主要介绍 JUC (Java.util.concurrent) 中些工具的使用方法、 各自特点及它们的内部实现原理。...
- `AtomicInteger`、`AtomicLong`等原子类提供了一种在不使用锁的情况下实现线程安全的方式,通过底层的CAS(Compare and Swap)操作保证了原子性,适用于简单的原子更新操作。 5. **Future和Callable接口** - `...
### Java后端开发与JUC并发编程 #### 进程和线程的概念 在Java后端开发与JUC(Java.util.concurrent包)并发编程中,进程和线程是两个核心概念。 **进程**是系统进行资源分配的基本单位,具有并发性、异步性、...
Java-JUC-多线程进阶resources是 Java 并发编程的高级课程,涵盖了 Java 中的并发编程概念、线程安全、锁机制、集合类、线程池、函数式接口、Stream流式计算等多个方面。 什么是JUC JUC(Java Utilities for ...
Java并发编程中的JUC线程池是Java程序员必须掌握的关键技术之一,它允许开发者高效地管理并发执行的任务,充分利用多核处理器的性能。线程池的出现解决了在并发环境中线程创建、销毁带来的开销,提高了系统资源的...
《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...