适用场景:
你需要从数据库加载股票交易的安全代码并且考虑到性能进行了缓存。这些安全代码需要每30分钟刷新一次。在这里缓存数据需要一个单独的写线程进行生成和刷新,并且被若干读进程进行读取。这种情况下,你又要如何来保证你的读写方案做到良好的扩展和线程安全呢?
解决方案:java.util.concurrent.locks包提供了可以并行执行的写锁以及被单个线程独占的写锁的实现。这个ReadWriterLock(读写锁)接口包含了一对关联的锁,一个只读锁和一个写锁。其中readLock()(读锁)可以被多个进行读操作的线程同时持有,然而writeLock(写锁)确实独占的。通常情况下,这种实现方式可以在与以下场景下相对于互斥锁可以显著的提高性能以及程序的可扩展性
场景一:
读操作以及读的频率要频繁于写操作和写的频率。
场景二:执行场景依赖底层操作系统 -- 例如在多核处理器上为了实现更好的并行执行
ConcurrentHashMap是另外一个可以在读多于写操作的情况下提高性能的数据结构。ConcurrentHashMap允许并发读取以及独占的更新或者插入操作。
这里有一些技术可以让你更加了解锁的实现
Q:什么是可重入锁?
A:可重入锁是作为传统“等待-唤醒”方法的替代物出现的。它的基本实现理念是,每一个需要进入临界区的线程需要获取锁并且应该在随后的操作完成后释放锁。ReetrantLock可以通过减少“synchronized”关键字同步来提供更好的并发
reetrant这个单词其实意味着如果线程试图进去当前线程已持有锁保护的并且需要同步的代码块,线程会默许操作是允许的,并且不会在线程退出第二个(随后的)同步代码区释放持有的锁,只有在线程退出被同一锁保护的首先进入的同步代码块时才会释放锁,因为锁本身维护着锁的获取次数,并且如果一个已经获取锁的线程需要重新获取锁,维护的锁的大小会增加并且当前线程需要释放两次才能真正的释放锁,写线程可以获取读锁(优先级比读线程高) ---但是读线程却不能获取写锁,如果读线程尝试获取写锁,那将是永不能得到的(只能双眼包含热泪的望着).
Q:在释放锁的时候需要的注意事项?
A:锁的释放操作需要在finally块中进行,否则如果程序出现异常,那您的那把锁可能永远丢不掉(哈哈!)
Q:什么理由使你在已有synchronized关键字的情况下仍然选择使用重入锁?并且都能从中获取哪些好处?
A:重入锁在并发读的时候有更好的可扩展性。前面已经说过,java.util.concurrent.lock包中的若干锁的实现是针对于高级用户的高级工具,并且也在上面讨论过的特定场景下适用。通常来说,如果没有如下的条件作为前提还是规劝您老实的使用synchronization:
1.读操作的个数远多于写操作
2.可以通过一些测试数据证实在特定场景下同步是主要的扩展瓶颈
3.诸如超时锁等待,可中断的锁等待,非阻塞结构的,或者有多个条件需要判断,或者需要轮询锁这些普通锁不具有的或者实现不如此的情况下
Q:什么是公平锁和非公平锁?
A:ReetrantLock的构造器有一个boolean类型的参数可以用来指示是否使用公平锁还是非公平锁。公平锁是获取锁的顺序和线程请求锁的线程一致。也就是说,当前的写锁释放的时候,无论是一个写线程等待多久,只要有等待队列中有读线程在写线程之前,读操作的线程就会首先得到读锁。但是当你使用的是非公平锁的时候,获取锁的顺序就不一定是线程请求的顺序了。
如果读线程是活跃的,那么只有写线程已经获取写锁并且释放写锁后读线程才能被授予读锁,下面是一个读写锁的示例。
import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; public class SharedData<T> { private List<Integer> numbers = new ArrayList<Integer>(20); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public List<Integer> getTechnologies() { return numbers; } public void writeData(Integer number) { String threadName = Thread.currentThread().getName(); lock.writeLock().lock(); //如果没有其他线程持有读锁和写锁,获取写锁 //写锁只允许一个线程持有 System.out.println("threads waiting for read/write lock: " + lock.getQueueLength()); // This should be always one System.out.println("writer locks held " + lock.getWriteHoldCount()); try { numbers.add(number); System.out.println(">>>>>" + threadName + " writing: " + number); Thread.sleep(2000); // 线程休眠两秒钟 } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(threadName + " releasing write lock"); lock.writeLock().unlock(); //释放锁 } } public void readData() { String threadName = Thread.currentThread().getName(); lock.readLock().lock();//在没有线程持有写锁的情况下获取读锁 //允许并发读操作 System.out.println("threads waiting for read/write lock: " + lock.getQueueLength()); System.out.println("reader locks held " + lock.getReadLockCount()); try { for (Integer num: numbers) { System.out.println("<<<<<<<" + threadName + " reading: " + num); } Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally{ System.out.println(threadName + " releasing read lock"); lock.readLock().unlock(); //释放锁 } } }
下一步,定义读和写线程类。
public class Reader<T> extends Thread { private SharedData<T> sharedData; public Reader(SharedData<T> sharedData) { this.sharedData = sharedData; } @Override public void run() { sharedData.readData(); } }
public class Writer<T> extends Thread { private boolean oddNumber = true; private SharedData<T> sharedData; public Writer(SharedData<T> sharedData, boolean oddNumber ) { this.sharedData = sharedData; this.oddNumber = oddNumber; } @Override public void run() { for(int i=1; i<=2; i++) { if(!oddNumber && i%2 == 0) { sharedData.writeData(i); } else if (oddNumber && !(i%2 == 0)){ sharedData.writeData(i); } } } }
最后,ReadWriteProcessor(读写主线程)类需要生成读写线程并为线程传递SharedData.
会有如下输出结果:
threads waiting for read/write lock: 0 writer locks held 1 >>>>>oddWriter writing: 1 oddWriter releasing write lock threads waiting for read/write lock: 3 writer locks held 1 >>>>>evenWriter writing: 2 evenWriter releasing write lock threads waiting for read/write lock: 2 threads waiting for read/write lock: 1 reader locks held 3 threads waiting for read/write lock: 0 reader locks held 3 reader locks held 2 <<<<<<<reader1 reading: 1 <<<<<<<reader2 reading: 1 <<<<<<<reader3 reading: 1 <<<<<<<reader2 reading: 2 <<<<<<<reader1 reading: 2 <<<<<<<reader3 reading: 2 reader1 releasing read lock reader3 releasing read lock reader2 releasing read lock
其实还有下面的一些java.util.concurrent包下的类可以在其他现实场景下起到很好的作用.
1.CountDownLatch
2.CyclicBarrier
3.Semaphore
4.Atomic 若干类
相关推荐
Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程Java 并发性和多线程
说的是有关JAVA并发中的线程安全性问题
Java 模拟线程并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发
并发编程、线程安全样例 代码 包含了并发编程的多种业务场景demo测试代码
高并发之-SimpleDateFormat类的线程安全问题和解决方案.docx
proactive 多线程并发解决方案,这本书主要是入门级的程序员使用。。
Java 高并发多线程编程系列案例代码 & 教程 & 面试题集锦! !! 包括但不限于线程安全性, atomic包下相关类、CAS原理、Unsafe类、synchronized关键字等的使用及注意事项,
高并发解决方案
java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...
并发服务器-多线程服务器详解
详细介绍java并发编程相关知识: 基础知识 并发与并行 Java并发演进历史 Java并发模型 线程模型 存储模型 JVM同步原语 volatile CAS 线程安全 保护“共享数据” 低级并发工具 原子变量 锁...
互联网高并发解决方案互联网高并发解决方案互联网高并发解决方案
本文来自于csdn,本文主要从分布式的原因,事务特性,和解决方案中深入理解了分布式事务,希望对您的学习有所帮助。 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的...
第4章线程安全性 第5章安全发布对象 第6章线程安全策略 第7章J.U.C之AQS 第8章J.U.C组件拓展 第9章线程调度-线程池 第10章多线程并发拓展 第11章高并发之扩容思路 第12章高并发之缓存思路 第13章高并发之消息队列...
《java并发编程实战》读书笔记-第2章-线程安全性,脑图形式,使用xmind8制作 包括引言、线程安全性定义、原子性、加锁机制、使用锁保护状态、活跃性与性能等内容
能学到什么:常见Java高并发多线程面试问题及在相关场景下如何处理和解决这些问题。 阅读建议:通过快速阅读全文并在过程中标记自己不熟悉的问题,定期复习来提高理解和记忆。通过反复学习和复习,达到消化吸收和...
高并发是指在同一个时间点,有很多用户同时访问URL地址,比如...这里主要讲述的是在并发请求下的数据逻辑处理的接口,如何保证数据的一致性和完整性,这里的并发可能是大量用户发起的,也可能攻击者通过并发工具发起的
同时,并发编程更广泛,它包括多线程编程,还包括其他并发技术和编程范式,以解决各种并发问题和场景。 总之,多线程是并发编程的一种具体实现方式,用于处理同一程序内的多个线程。而并发编程则更广泛,涵盖了处理...
本书注重理论与实践相结合,通过大量的示例代码和案例分析,帮助读者更好地理解和掌握并发编程的技巧和精髓。同时,书中还提供了丰富的练习和思考题,帮助读者巩固所学知识,提升编程实践能力。
基于Qt的多线程并发服务器 incomingConnection(qintptr socketDescriptor)检测