在java中,在并发环境中使用的最佳队列缓存是什么
我需要在多线程环境中具有消息队列缓存,在多线程环境中,每秒有数千个请求 如果队列不为空,则每个请求线程应从队列中弹出消息(并且任何线程都不应获取重复消息),并在db中递减一个计数器。如果队列为空,则线程将从db中提取固定数量的消息(例如100)并填充队列缓存(其他线程应在队列填充时等待)然后弹出一条消息+以db为单位递减一个计数器并返回 db中的弹出和递减计数器应同步,以避免任何不一致 因此,从需求来看,很明显,缓存将有更多的读和删除(弹出)操作,而写操作更少(仅当缓存为空时) 现在我有一个同步的方法getMessage,其中有一个ArrayList,我在这个方法中执行上面的操作(如果为空,则弹出,否则取出,然后弹出),但是我显然面临很多争用问题 如果在读取/删除时,并发线程获得不同的锁,并且在写入时,锁应该位于整个缓存上,那么这将减少我的争用问题在java中,在并发环境中使用的最佳队列缓存是什么,java,multithreading,caching,concurrency,Java,Multithreading,Caching,Concurrency,我需要在多线程环境中具有消息队列缓存,在多线程环境中,每秒有数千个请求 如果队列不为空,则每个请求线程应从队列中弹出消息(并且任何线程都不应获取重复消息),并在db中递减一个计数器。如果队列为空,则线程将从db中提取固定数量的消息(例如100)并填充队列缓存(其他线程应在队列填充时等待)然后弹出一条消息+以db为单位递减一个计数器并返回 db中的弹出和递减计数器应同步,以避免任何不一致 因此,从需求来看,很明显,缓存将有更多的读和删除(弹出)操作,而写操作更少(仅当缓存为空时) 现在我有一个同步
在这种情况下,哪种java缓存应该是最好的?由于这个原因,在负载中我面临低性能。请给我一些更好的主意 而不是
同步
您可以查看可重入锁
。对于您的需求,Java中有一个ReentrantReadWriteLock
类。有关详细信息,您可以参考Java文档。基本上,在读线程访问数据的次数多于写线程访问数据的情况下,建议使用ReentrantReadWriteLock
根据这个实现,多个线程可以读取相同的资源而无需锁定。但是对资源的一次写入操作将锁定它,并且不允许同时进行其他读取或写入操作
在线提供了许多示例,您可以参考实现ReentrantReadWriteLock
样本:
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWrite {
public static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
public static StringBuffer message = new StringBuffer("a");
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new Reader(lock, message));
Thread t2 = new Thread(new WriterA(lock, message));
Thread t3 = new Thread(new WriterB(lock, message));
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Reader implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public Reader(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
if(lock.isWriteLocked()) {
System.out.println("I'll take the lock from Write");
}
lock.readLock().lock();
System.out.println("ReadThread " + Thread.currentThread().getId() + " ---> Message is " + message.toString() );
lock.readLock().unlock();
}
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WriterA implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public WriterA(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
try {
lock.writeLock().lock();
message.append("a");
}finally {
lock.writeLock().unlock();
}
}
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WriterB implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public WriterB(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
try {
lock.writeLock().lock();
message.append("b");
}finally {
lock.writeLock().unlock();
}
}
}
}
package concurrency.reentrantreadwrite;
导入java.util.concurrent.locks.ReentrantReadWriteLock;
公共类可重入写入{
public static ReentrantReadWriteLock lock=新的ReentrantReadWriteLock(true);
公共静态StringBuffer消息=新StringBuffer(“a”);
公共静态void main(字符串[]args)引发InterruptedException{
线程t1=新线程(新读卡器(锁、消息));
线程t2=新线程(新写线程(锁、消息));
线程t3=新线程(新WriterB(锁,消息));
t1.start();
t2.start();
t3.start();
t1.join();
t2.连接();
t3.join();
}
}
包并发性.reentrantreadwrite;
导入java.util.concurrent.locks.ReentrantReadWriteLock;
公共类读取器实现可运行
{
REENTRANDREADWRITELOCK lock=null;
StringBuffer消息=null;
公共读取器(可重传写锁,StringBuffer消息){
this.lock=锁;
this.message=消息;
}
公开募捐
{
对于(int i=0;i而不是Synchronize
您可以查看reentrantlock
。根据您的需求,Java中有一个reentrandredwritelock
类。您可以参考Java文档了解详细信息。基本上,如果数据被比w更多的读卡器线程访问,建议使用reentrandredwritelock
里特线
根据这个实现,多个线程可以读取同一个资源而无需锁定。但是对该资源的单个写入操作将锁定该资源,并且不允许同时执行其他读取或写入操作
在线提供了许多示例,您可以参考实现ReentrantReadWriteLock
样本:
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWrite {
public static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
public static StringBuffer message = new StringBuffer("a");
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new Reader(lock, message));
Thread t2 = new Thread(new WriterA(lock, message));
Thread t3 = new Thread(new WriterB(lock, message));
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Reader implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public Reader(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
if(lock.isWriteLocked()) {
System.out.println("I'll take the lock from Write");
}
lock.readLock().lock();
System.out.println("ReadThread " + Thread.currentThread().getId() + " ---> Message is " + message.toString() );
lock.readLock().unlock();
}
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WriterA implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public WriterA(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
try {
lock.writeLock().lock();
message.append("a");
}finally {
lock.writeLock().unlock();
}
}
}
}
package concurrency.reentrantreadwrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WriterB implements Runnable
{
ReentrantReadWriteLock lock = null;
StringBuffer message = null;
public WriterB(ReentrantReadWriteLock lock, StringBuffer message) {
this.lock = lock;
this.message = message;
}
public void run()
{
for(int i = 0; i<= 10; i ++)
{
try {
lock.writeLock().lock();
message.append("b");
}finally {
lock.writeLock().unlock();
}
}
}
}
package concurrency.reentrantreadwrite;
导入java.util.concurrent.locks.ReentrantReadWriteLock;
公共类可重入写入{
public static ReentrantReadWriteLock lock=新的ReentrantReadWriteLock(true);
公共静态StringBuffer消息=新StringBuffer(“a”);
公共静态void main(字符串[]args)引发InterruptedException{
线程t1=新线程(新读卡器(锁、消息));
线程t2=新线程(新写线程(锁、消息));
线程t3=新线程(新WriterB(锁,消息));
t1.start();
t2.start();
t3.start();
t1.join();
t2.连接();
t3.join();
}
}
包并发性.reentrantreadwrite;
导入java.util.concurrent.locks.ReentrantReadWriteLock;
公共类读取器实现可运行
{
REENTRANDREADWRITELOCK lock=null;
StringBuffer消息=null;
公共读取器(可重传写锁,StringBuffer消息){
this.lock=锁;
this.message=消息;
}
公开募捐
{
对于(int i=0;i摘要
当你面对一个架构问题时,你应该提取一个“模型”并抽象出任何带有接口的外部部件/系统。因此,你可以很容易地从让这些接口等待一个固定的时间开始,然后是随机的,接下来是采样的持续时间等等
它使你更清楚你的算法/系统是如何工作的,并使重构/调整变得非常容易,直到“模型”令人满意。随着你的模型变得更清晰,它也使你更容易插入监视并显示你必须监视的内容/位置。如果需要,我可以添加更完整的解释
从您的问题来看,您应该在“缓存消耗”和“缓存生成”之间添加更好的分离。您还必须使用非阻塞算法并更平滑地推送到“缓存”。请注意,“非阻塞”这并不意味着你的线程永远不会饿死;如果没有什么可处理的,它们就必须处于休眠状态。但是,它们不应该互相阻塞
外部系统
正如总结中提到的,必须抽象外部系统,并且必须使用默认值/花费的时间。因此,您可以使用相同的场景/模型,但不能使用changi
class Model<T> {
private Storage<T> storage;
private int fetchSize;
private Consumer<T> processor;
/**
* while active:
* Try to poll a message from cache
* if none:
* Fetch from storage
* Put fetched messages to cache
* else:
* Process message
* Update storage according to processed message
**/
public void consume(BooleanSupplier active) {
try {
while (active.getAsBoolean()) {
T message = cache.poll();
if (message == null) {
// Fetch new messages from storage
List<T> newMessages = new ArrayList<>(fetchSize);
synchronized (storage) {
storage.drainTo(newMessages, fetchSize);
}
// Push new messages to cache
for (T newMessage : newMessages) {
cache.put(newMessage);
}
} else {
processor.accept(message);
storage.update(message);
}
}
} catch (InterruptedException e) {
if (!active.getAsBoolean()) {
throw new RuntimeException(e);
}
}
}
}
class ImprovedModel<T> {
private Storage<T> storage;
private int fetchSize;
private Consumer<T> processor;
private StampedLock lock = new StampedLock();
/**
* while active:
* Try to poll a message from cache
* if none:
* if storage available:
* Transfer from storage to cache
* else:
* Take message from cache
* if message:
* Process message
* Update storage according to processed message
**/
public void consume(BooleanSupplier active) {
try {
while (active.getAsBoolean()) {
T message = cache.poll();
if (message == null) {
// Check storage availability
int stamp = lock.tryWriteLock();
try {
if (stamp != 0) {
storage.drainTo(cache, fetchSize);
} else {
message = cache.take();
}
} finally {
lock.unlockWrite(stamp);
}
}
if (message != null) {
processor.accept(message);
storage.update(message);
}
}
} catch (InterruptedException e) {
if (!active.getAsBoolean()) {
throw new RuntimeException(e);
}
}
}
}