并发编程二:普通锁
来源:互联网 发布:yum mirrorlist 编辑:程序博客网 时间:2024/05/11 19:19
并发编程之普通锁
介绍
锁是一种线程同步机制,和synchronized同步代码块一样,但是锁具备一些更高级和` 细粒度的对同步的控制。锁和syncronized同步方法/代码块本是相同的,它们的目的都是为了保证共享资源被安全的访问。
锁(lock)作为用于保护临界区(critical section)的一种机制,被广泛应用在多线程程序中。无论是 Java 语言中的 synchronized 关键字,还是使用并发包的Lock实现,都是多线程应用开发人员手中强有力的工具。但是强大的工具通常是把双刃剑,过多或不正确的使用锁,会导致多线程应用的性能下降。
备注:锁这种机制和理念并不是Java所特有的,这只是一种思想其它任何语言都可以实现。
Java Lock介绍和使用
从jdk1.5之后在java.util.concurrent.locks包下包含了几个锁的实现,所以我们能够很轻易的使用锁来控制同步,我们无需自己实现锁的功能。但是我们任然需要知道如何使用他们,并且知道他们是如何实现依然非常的重要。
Lock接口定义
public interface Lock { //获得锁资源方法 void lock(); //释放锁资源方法 void unlock(); //建立一个对象监视器 Condition newCondition(); //尝试获得锁资源 boolean tryLock(); //指定时间内尝试获取锁资源 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void lockInterruptibly() throws InterruptedException;}备注:使用锁最后一定要释放锁资源,否则资源就可能永远得不到释放。典型使用锁代码: Lock lock = ...; lock.lock(); try { //访问共享资源 } finally { //释放共享资源 lock.unlock(); }
一个简单例子引出锁
定义一个Counter类,其有一个方法就是自增count值。如果多线程场景下访问可能就会出问题。因此需要最自增方法做同步处理。
/** * * @author xuyi * @Time 2016年8月13日 上午8:52:05 * @类名 Counter * @功能描述:一个简单的计算类,里面只包含一个自增方法和count属性 * @春风十里不如你 * @备注: */public class Counter{ // 统计数值 private int count; // 自增方法 public int incr() { return (count++);// 或者把++写在前面 }}备注:如果单线程环境访问这个对象并不会有任何问题,但是如果多线程访问则可能就会出问题了, 因为incr方法并没有使用同步机制,导致多个线程可以同时访问这个方法,那么就会导致他们的计算结果不正确了。因为该方法不是原子操作,所以没有同步机制还是会出现安全问题。
解决方案: 1、对共享资源进行同步 2、使操作变为原子性操作 3、将count申明为可见的原子性AtomicInteger类型然后再操作 这里我们只使用第一种方式来扩展锁知识。
使用synchronized同步机制
将上面的incr方法改为如下,使用synchronized同步代码块// 自增方法public int incr(){ synchronized (this) { return (count++);// 或者把++写在前面 }}
使用Lock锁
正如之前我们提到的Java已经给我们提供了很棒的Lock实现类,我们可以使用ReentrantLock对象来达到锁同步的目的。
public class Counter{ // 统计数值 private int count; // 申明一个ReentrantLock对象 private ReentrantLock lock = new ReentrantLock(); // 自增方法 public int incr() { lock.lock();//将资源上锁,保证资源访问时安全的 try { return (count++);// 或者把++写在前面 } finally { lock.unlock();//最终释放锁资源 } }}
自定义实现锁
虽然JDK给我们提供了ReentrantLock和ReentrantReadWriteLock这两个非常棒的锁实现,大部分场景下我们都能使用它们来解决很多问题。但是我们不仅要会用,更应该知道它们背后锁实现的原理。
简单的锁实现
/** * * @author xuyi * @Time 2016年8月13日 上午9:14:38 * @类名 SimpleLock * @功能描述:实现锁的基本功能 * @春风十里不如你 * @备注: */public class SimpleLock{ // 是否获得锁的标识 private boolean isLocked = false; // 获取锁资源 public void lock() { synchronized (this) { while (isLocked) {// 如果锁资源已经被别的线程占有了,那么就在此等待 try { wait(); } catch (InterruptedException e) { } } // 如果锁资源目前没有被别的资源占有,那么将锁标识位设置为true,表示锁资源被当前线程占有。 isLocked = true; } } // 释放锁资源 public void unlock() { synchronized (this) { // 将锁资源标识为空闲状态,然后通知其它等待线程来抢占资源 isLocked = false; notify(); } } // 其实该锁实现还是借助了JVM的synchronized同步机制,只是进行了稍加封装。}
备注:该简单锁实现是比较简单粗暴的,因为它没有考虑同个对象资源里面,方法可能会存在互相调用,而导致锁重入的情况。
/** * * @author xuyi * @Time 2016年8月13日 上午9:24:04 * @类名 Resource * @功能描述:存在锁重入的方法 * @春风十里不如你 * @备注: */public class Resource{ // 定义一个简单锁对象 private SimpleLock lock = new SimpleLock(); public void outter() { lock.lock(); try { // ... inner(); // ... } finally { lock.unlock(); } } public void inner() { lock.lock(); try { // ... } finally { lock.unlock(); } }}备注:对于这个对象如果调用outter方法,使用SimpleLock来处理那么就会导致死锁了,所以此时我们应该升级我们的简单锁了。
可重入锁实现
关于重入锁,我们可以从上面那个例子看出来其到底是什么意思。Java中的synchronized同步块是可重入的。这意味着如果一个java线程进入了代码中的synchronized同步块,并因此获得了该同步块使用的同步对象对应的管程上的锁,那么这个线程可以进入由同一个管程对象所同步的另一个java代码块。ReentrantLock也是可重入锁的实现
/** * * @author xuyi * @Time 2016年8月13日 上午9:34:01 * @类名 SimpleReentrantLock * @功能描述:简单可重入锁的实现 * @春风十里不如你 * @备注: */public class SimpleReentrantLock{ // 是否占有锁资源的标识位 private boolean isLocked = false; // 当前锁资源被哪个线程持有 private Thread lockedByThread = null; // 某线程上锁次数 private int lockCount = 0; public void lock() { synchronized (this) { Thread currentThread = Thread.currentThread(); while (isLocked && currentThread != lockedByThread) {// 如果锁资源被占有了,并且当前线程并不是占有锁资源的线程。 try { wait(); } catch (InterruptedException e) { } } isLocked = true; lockCount++; lockedByThread = currentThread; } } public void unlock() { if (Thread.currentThread() == this.lockedByThread) {// 如果当前线程就是获得锁资源的线程,那么就lockCount进行自减. lockCount--; if (lockCount == 0) {// 如果lockCount恢复到0时就释放资源 isLocked = false; notify(); } } }}
总结
锁的概念在很多场景都是会存在的不经事编程语言、数据库、操作系统、文件的其本质就是对共享资源的并发访问。其实java提供的ReentrantLock实现已经非常棒了,多看看其源码实现原理有助于提升自己的对编程模型的认识。
参考
1、JDK源码实现
2、http://tutorials.jenkov.com/java-concurrency/locks.html
3、https://www.ibm.com/developerworks/cn/java/j-lo-lock/
0 0
- 并发编程二:普通锁
- 高并发编程二
- Java并发编程二
- 普通二本生如何学编程
- 并发编程实践二:AbstractQueuedSynchronizer
- JAVA并发编程笔记二
- Java并发编程(二)
- Java并发编程总结二
- java并发编程之二
- 并发编程(二)-访问共享资源
- Erlang 并发编程基础二
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
- Java并发编程实战---- AQS(二):获取锁、释放锁
- JAVA并发编程(二)内置锁和对象共享
- Java并发编程系列(二)----synchronized锁详解
- Java并发编程(二)多线程编程
- java并发编程(二十)--并发新特性—Lock锁和条件变量
- STL vector用法介绍
- Android 标题栏与状态栏同色
- HDU 5787 (数位dp)(2016多校联赛)
- Linux下编译openJDK源码
- ajax的同步与异步
- 并发编程二:普通锁
- 《VR入门系列教程》之20---使用Oculus移动端SDK
- Android中常见的内存泄漏
- static和const,及final关键字的使用
- 4.Android使用ksoap2-android调用WebService
- Android——获取手机当前信号强度(dbm/asu值)
- 《VR入门系列教程》之21---使用Unity开发GearVR应用
- 坚持#第13天~触控培训结束 满载而归
- 1342433