适用场景:
你需要从数据库加载股票交易的安全代码并且考虑到性能进行了缓存。这些安全代码需要每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 若干类
相关推荐
通过分析和学习这些示例,我们可以更好地理解如何在实际项目中应用多线程和高并发技术。 总之,多线程和高并发是提升软件性能和扩展性的重要手段。在实践中,我们需要充分理解它们的原理,掌握相应的编程技巧,以及...
ConcurrentHashMap通过分段锁机制实现并发控制,相比于synchronized Map,其在高并发环境下有更好的性能。 3. 使用HashTable:虽然HashTable是线程安全的,但由于其所有操作都是同步的,所以在多线程并发访问时,...
虽然提供了基本的线程安全性,但它们不是高度优化的并发解决方案,因为所有操作都需要全局锁定,可能导致性能瓶颈。 2. 并发集合(Concurrent Collections): Java的`java.util.concurrent`包提供了更为高效且...
懒汉式单例在实现线程安全性时,通常采用双重检查锁定技术: ```java public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance()...
在高并发场景下,System....通过使用 `java.time` 包中的类或自定义线程安全的解决方案,我们可以获得更好的性能和可靠性。在开发过程中,应该根据具体需求和测试结果来做出选择,以确保系统的高效运行。
在Java编程中,集合框架是数据管理的核心部分。...此外,Java并发库(如`ConcurrentHashMap`、`CopyOnWriteArrayList`等)提供了更高效且线程安全的解决方案,可以在性能和线程安全之间找到更好的平衡。
在JDK5中,Java引入了一套强大的多线程并发库,极大地提升了多线程编程的效率和安全性。这个库提供了丰富的类和接口,使得开发者能够更好地控制线程的执行、同步和通信。 1. **线程** - **线程与进程的关系**:...
1. **ConcurrentHashMap**:线程安全的哈希表,比 synchronized HashMap 性能更好。 2. **ArrayList vs LinkedList**:面试中常问的问题,它们在并发场景下的优缺点,以及何时选择哪个。 六、JVM与内存模型 1. **...
- **优势**:减少线程管理的复杂度,提高线程安全性,优化资源利用效率。 ##### 6. 空中网挑选实习生的面试题 - **题型示例**:这些题目旨在考察应聘者对Java线程技术的理解深度和解决问题的能力,通常涉及线程...
进一步分析了保证多线程运行时的安全性措施及常见问题的解决方案,涵盖线程调度、死锁预防等多个方面。紧接着讨论了几种典型的并发结构设计如线程池和同步机制,还阐述了线程组的特点及为何不推荐使用,着重介绍了...
它们已经被并发容器如`ConcurrentHashMap`和`CopyOnWriteArrayList`取代,后者在并发访问时有更好的性能。 **AQS(AbstractQueuedSynchronizer)**是Java并发库的基础,它提供了一种基于FIFO队列的等待/通知机制,...
7. **线程安全问题**:分析可能出现的并发问题,如竞态条件、死锁、活锁和饥饿等,并给出解决方案。 8. **线程实战**:通过具体的示例展示如何在实际项目中使用多线程,例如在Web服务器、大数据处理等场景的应用。 ...
### Java并发编程与高并发解决方案 #### 一、并发编程基础 ##### 1.1 并发与并行的区别 在计算机科学领域,“并发”(Concurrency)和“并行”...希望本文能帮助大家更好地理解Java并发编程的相关概念和技术栈。
- **示例代码:** 书中提供了大量的示例代码,涵盖各种并发编程的典型应用场景,帮助读者更好地理解并发编程的技术细节。 - **案例分析:** 分析真实世界中的并发编程问题,展示如何运用理论知识解决实际问题。 - **...
涵盖线程的基础概念(如进程、线程定义)、多线程的作用及应用场景、多线程实现的三种方式(继承Thread类、实现Runnable接口、实现Callable接口结合FutureTask)及各自的优缺点对比、线程生命周期中的常用成员方法...
"更好的管理线程间的跳转的库" 提供了一种解决方案,通过开源项目的形式来帮助开发者更高效地处理这一问题。 这个压缩包文件"更好的管理线程间的跳转的库 .zip"包含了一个名为"ObservableScheduler-master"的子...
在计算机科学中,多线程和线程池是并发编程中的关键概念,它们极大地提高了程序的执行效率,尤其是在处理大量...在实践中,还需要考虑线程安全、同步机制以及任务调度策略等因素,以实现更高效、稳定的并发解决方案。
内容概要:本文详细介绍了单例模式的基本概念以及它的重要性和广泛的应用背景,重点探讨了在 ...此外,掌握这几种不同的实现可以更好地理解 Go 在多线程环境中的特性和优势,为解决更多高并发场景问题打下坚实的基础。
针对以上提到的问题,有多种不同的解决方案可以确保`HashMap`在多线程环境下的安全性: 1. **使用并发安全的集合框架**:例如`Hashtable`和`ConcurrentHashMap`。 2. **使用`Collections.synchronizedMap()`方法**...