转发 :java线程:互斥锁与读写锁

原文链接:http://coolxing.iteye.com/blog/1236909

两种互斥锁机制:

1、synchronized

2、ReentrantLock

ReentrantLock是jdk5的新特性,采用ReentrantLock可以完全替代替换synchronized传统的锁机制,而且采用ReentrantLock的方式更加面向对象,也更加灵活,网上有很多关于对比两者锁方式的文章,这里就不多口舌了,大家baidu、google一下就水落石出了。在本博客中也写关于这两种锁方式实现的经典例子《生产者消费者》。

synchronized方式:《java线程:三种方式实现生产者消费者问题_1》

ReentranLock方式:《java线程:三种方式实现生产者消费者问题_2》

关于读写锁,用语言解释不如直接用代码诠释,以下通过两个例子讲述读写锁以及读写锁的使用:

例子1:

[java] view plaincopy

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import java.util.concurrent.locks.ReadWriteLock;
  4. import java.util.concurrent.locks.ReentrantReadWriteLock;
  5. /**
  6. * @author amber2012
  7. *
  8. * 读写锁:ReadWriteLock
  9. *
  10. * 在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在
  11. * 写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的
  12. * 不一致性。
  13. *
  14. * 这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这
  15. * 样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高
  16. * 于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期
  17. * 间上锁就行了。
  18. *
  19. * 对于这种情况,读写锁则最好的解决方案!
  20. *
  21. * 读写锁的机制:
  22. *      "读-读"不互斥
  23. *      "读-写"互斥
  24. *      "写-写"互斥
  25. *
  26. * 即在任何时候必须保证:
  27. *      只有一个线程在写入;
  28. *      线程正在读取的时候,写入操作等待;
  29. *      线程正在写入的时候,其他线程的写入操作和读取操作都要等待;
  30. *
  31. * 以下是一个缓存类:用于演示读写锁的操作:重入、降级
  32. */
  33. public class CachedData {
  34. // 缓存都应该是单例的,在这里用单例模式设计:
  35. private static CachedData cachedData = new CachedData();
  36. private final ReadWriteLock lock = new ReentrantReadWriteLock();//读写锁
  37. private Map<String, Object> cache = new HashMap<String, Object>();//缓存
  38. private CachedData(){
  39. }
  40. public static CachedData getInstance(){
  41. return cachedData;
  42. }
  43. // 读取缓存:
  44. public Object read(String key) {
  45. lock.readLock().lock();
  46. Object obj = null;
  47. try {
  48. obj = cache.get(key);
  49. if (obj == null) {
  50. lock.readLock().unlock();
  51. // 在这里的时候,其他的线程有可能获取到锁
  52. lock.writeLock().lock();
  53. try {
  54. if (obj == null) {
  55. obj = "查找数据库"; // 实际动作是查找数据库
  56. // 把数据更新到缓存中:
  57. cache.put(key, obj);
  58. }
  59. } finally {
  60. // 当前线程在获取到写锁的过程中,可以获取到读锁,这叫锁的重入,然后导致了写锁的降级,称为降级锁。
  61. // 利用重入可以将写锁降级,但只能在当前线程保持的所有写入锁都已经释放后,才允许重入 reader使用
  62. // 它们。所以在重入的过程中,其他的线程不会有获取到锁的机会(这样做的好处)。试想,先释放写锁,在
  63. // 上读锁,这样做有什么弊端?--如果这样做,那么在释放写锁后,在得到读锁前,有可能被其他线程打断。
  64. // 重入————>降级锁的步骤:先获取写入锁,然后获取读取锁,最后释放写入锁(重点)
  65. lock.readLock().lock();
  66. lock.writeLock().unlock();
  67. }
  68. }
  69. } finally {
  70. lock.readLock().unlock();
  71. }
  72. return obj;
  73. }
  74. }

例子2:

[java] view plaincopy

    1. import java.util.Map;
    2. import java.util.TreeMap;
    3. import java.util.concurrent.locks.Lock;
    4. import java.util.concurrent.locks.ReadWriteLock;
    5. import java.util.concurrent.locks.ReentrantReadWriteLock;
    6. import javax.xml.crypto.Data;
    7. /**
    8. * @author amber2012
    9. *
    10. * jdk文档中关于ReentrantReadWriteLock类使用的一个很好的例子,以下是具体的介绍:
    11. *
    12. * 在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection
    13. * 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下
    14. * 是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
    15. */
    16. public class RWDictionary {
    17. private final Map<String, Data> map = new TreeMap<String, Data>();
    18. private final ReadWriteLock rwl = new ReentrantReadWriteLock();
    19. private final Lock readLock = rwl.readLock();
    20. private final Lock writeLock = rwl.writeLock();
    21. public Data get(String key) {
    22. readLock.lock();
    23. try {
    24. return map.get(key);
    25. } finally {
    26. readLock.unlock();
    27. }
    28. }
    29. public String[] allKeys() {
    30. readLock.lock();
    31. try {
    32. return (String[]) map.keySet().toArray();
    33. } finally {
    34. readLock.unlock();
    35. }
    36. }
    37. public Data put(String key, Data value) {
    38. writeLock.lock();
    39. try {
    40. return map.put(key, value);
    41. } finally {
    42. writeLock.unlock();
    43. }
    44. }
    45. public void clear() {
    46. writeLock.lock();
    47. try {
    48. map.clear();
    49. } finally {
    50. writeLock.unlock();
    51. }
    52. }
    53. }
时间: 2024-10-14 03:22:02

转发 :java线程:互斥锁与读写锁的相关文章

信号量、互斥锁,读写锁和条件变量的区别

信号量强调的是线程(或进程)间的同步:“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都 在sem_wait的时候,就阻塞在那里).当信号量为单值信号量是,也可以完成一个资源的互斥访问.有名信号量:可以用于不同进程间或多线程间的互斥与同步 创建打开有名信号量 sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag

原子操作、互斥锁、读写锁

原子操作 package main import ( "fmt" "sync" "sync/atomic" //原子操作,比读写锁和互斥锁都要快,原子操作在用户态,其他锁在内核态 "time" ) var w sync.WaitGroup var count int32 func main() { w.Add(1) start := time.Now().UnixNano() go func() { for i := 0; i

c++利用互斥锁实现读写锁

很简单就是在读的时候把写的锁锁住就好了 class readwrite_lock { public: readwrite_lock() : read_cnt(0) { } void readLock() { read_mtx.lock(); if (++read_cnt == 1) write_mtx.lock(); read_mtx.unlock(); } void readUnlock() { read_mtx.lock(); if (--read_cnt == 0) write_mtx.u

Go36-26-互斥锁与读写锁

从同步讲起 相比于Go语言宣扬的"用通讯的方式共享数据",通过共享数据的方式来传递信息和协调线程运行的做法其实更加主流.本篇就是讨论一些与多线程.共享资源以及同步有关的知识. sync包,就是一个与并发编程关系紧密的代码包.这里"sync"的中文意思就是"同步". 重要的并发编程概念 这里会讲一些重要的并发编程概念:竞态条件.临界区.互斥量.死锁.死锁会在互斥锁里引出. 一旦数据被多个线程共享,那么就很可能会产生争用和冲突的情况.这种情况也被称为

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

C# lock 语法糖实现原理--《.NET Core 底层入门》之自旋锁,互斥锁,混合锁,读写锁

原文:C# lock 语法糖实现原理--<.NET Core 底层入门>之自旋锁,互斥锁,混合锁,读写锁 在多线程环境中,多个线程可能会同时访问同一个资源,为了避免访问发生冲突,可以根据访问的复杂程度采取不同的措施 原子操作适用于简单的单个操作,无锁算法适用于相对简单的一连串操作,而线程锁适用于复杂的一连串操作 原子操作 修改状态要么成功且状态改变,要么失败且状态不变,并且外部只能观察到修改前或者修改后的状态,修改中途的状态不能被观察到 .NET 中,System.Threading.Inte

内部锁、显示锁和读写锁

线程同步机制 线程同步机制是一套用于协调线程间的数据访问及活动的机制.该机制用于保障线程安全及实现这些线程的共同目标. java平台提供的线程同步机制: 锁 volatile关键字 final关键字 static关键字 其他(如:Object.wait()/Object.notify()等) 锁机制 锁机制 :将多线程并发访问共享数据转换为串行访问,一个共享数据每次只能被一个线程访问(获得锁),该线程访问结束后(释放锁)其他线程才能对其访问. 锁的获得 : 一个线程在访问数据前必须申请相应的锁.

Java线程:锁

一.锁的原理 Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁.获得一个对象的锁也称为获取锁.锁定对象.在对象上锁定或在对象上同步. 当程序运行到synchronized同步方法或代码块时该对象锁才起作用.一个对象只有一个锁.所以一个线程获得该所,就没有其他线程获得,直到第一个线程释放(或返回)锁.这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放.

Java线程与锁

Java线程与锁 本篇是 <深入理解Java虚拟机>的最后一章, 在此涉及到了线程安全, 但并不是如何从代码层次来实现线程安全, 而是虚拟机本身对线程安全做出了哪些努力, 在安全与性能之间又采取了哪些优化措施. 那么一步步来梳理这些概念. 三种线程概念--内核线程.轻量级进程.用户线程 参考 内核线程.轻量级进程.用户线程三种线程概念解惑(线程≠轻量级进程) Linux下的进程类别(内核线程.轻量级进程和用户进程)以及其创建方式--Linux进程的管理与调度(四) 内核线程(Kernel-Le