Java 当不再有线程持有某个对象时,如何从ConcurrentHashMap中删除项目?
假设我有一个临时集合(ConcurrentHashMap)来保存某个对象的引用,例如HttpSession。当至少有一个线程正在使用一个会话时(在请求时),它不能被删除。但是,当没有更多线程同时使用会话时,应该删除该会话以释放内存。我尝试实现一个类似的示例,但得到了一个NullPointerException。我做错了什么(Java 当不再有线程持有某个对象时,如何从ConcurrentHashMap中删除项目?,java,multithreading,concurrency,locking,Java,Multithreading,Concurrency,Locking,假设我有一个临时集合(ConcurrentHashMap)来保存某个对象的引用,例如HttpSession。当至少有一个线程正在使用一个会话时(在请求时),它不能被删除。但是,当没有更多线程同时使用会话时,应该删除该会话以释放内存。我尝试实现一个类似的示例,但得到了一个NullPointerException。我做错了什么( 类元素{ //AtomicInteger saldo=新的AtomicInteger(1000); 整数saldo=1000; } 类Sum实现可运行{ 萨尔多斯地图; 原
类元素{
//AtomicInteger saldo=新的AtomicInteger(1000);
整数saldo=1000;
}
类Sum实现可运行{
萨尔多斯地图;
原子整数n;
公共和(映射saldos,原子整数n){
this.saldos=saldos;
这个,n=n;
}
@凌驾
公开募捐{
Random rand=新的Random();
int r=兰特·耐克斯汀(1000);
Elem e=this.saldos.get(“saldo”);
//此处发生空指针异常!
已同步(e){
这个.n.incrementAndGet();
如果(r%2==0){
整数saldoLido=e.saldo;
e、 saldo+=r;
整数saldoAtual=e.saldo;
System.out.println(“莎尔多丽都:+saldoLido+”索马多:+r
+“莎尔多·阿图尔:+(莎尔多·阿图尔)+”
+System.currentTimeMillis());
}否则{
整数saldoLido=e.saldo;
e、 saldo-=r;
整数saldoAtual=e.saldo;
System.out.println(“saldo lido:+saldoLido+”subtraído:”
+r+“莎尔多舞曲:+(莎尔多舞曲)+”
+System.currentTimeMillis());
}
如果(此.n.decrementAndGet()==0)
这个.saldos.remove(“saldo”);
}
}
}
公共班机{
公共静态void main(字符串[]args)引发异常{
Map saldos=新的ConcurrentHashMap(20,0.9f,1);
AtomicInteger n=新的AtomicInteger(0);
saldos.put(“saldo”,newelem());
ExecutorService exec=Executors.newFixedThreadPool(20);
试一试{
对于(int i=0;i<20;++i)
exec.execute(新的Sum(saldos,n));
exec.shutdown();
而(!exec.isTerminated()){
System.out.println(“got元素:+saldos.get(“saldo”)+”+n);
}捕获(例外情况除外){
exec.shutdownNow();
例如printStackTrace();
}
}
}
扔掉它,使用一个java.util.WeakHashMap。
它已经完全满足了您的需求。扔掉它,使用一个java.util.WeakHashMap。
它已经满足了您的需求。我为您准备了一个工作示例,可以帮助您解决问题。我做了一个junit测试使其易于在您喜爱的IDE或其他地方运行
有几点需要注意
添加了一个倒计时闩锁,这样所有线程都将在executor服务关闭并打印结果之前完成
Elem使用原子整数,因此不再需要同步块
对代码最重要的修复是增加Sum类构造函数中的计数器,这样直到每个线程都有机会进行处理时,元素才从映射中删除。否则,一个线程可能会一直运行,并在其他线程有机会执行之前删除元素
--帕特里克
import java.util.Map;
导入java.util.Random;
导入java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.CountDownLatch;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.AtomicInteger;
导入org.junit.Test;
公共班机
{
@试验
public void testExecute()引发异常
{
int threadCount=20;
最终CountDownLatch threadsCompleteLatch=新的CountDownLatch(threadCount);
Map saldos=新的ConcurrentHashMap(线程数,0.9f,1);
AtomicInteger计数器=新的AtomicInteger(0);
元素=新元素();
saldos.put(“saldo”,元素);
ExecutorService exec=Executors.newFixedThreadPool(线程计数);
尝试
{
对于(int i=0;i class Elem {
// AtomicInteger saldo = new AtomicInteger(1000);
Integer saldo = 1000;
}
class Sum implements Runnable {
Map<String, Elem> saldos;
AtomicInteger n;
public Sum(Map<String, Elem> saldos, AtomicInteger n) {
this.saldos = saldos;
this.n = n;
}
@Override
public void run() {
Random rand = new Random();
int r = rand.nextInt(1000);
Elem e = this.saldos.get("saldo");
//Null Pointer Exception occurs here!
synchronized (e) {
this.n.incrementAndGet();
if (r % 2 == 0) {
Integer saldoLido = e.saldo;
e.saldo += r;
Integer saldoAtual = e.saldo;
System.out.println("saldo lido: " + saldoLido + " somado: " + r
+ " saldo atual: " + (saldoAtual) + " "
+ System.currentTimeMillis());
} else {
Integer saldoLido = e.saldo;
e.saldo -= r;
Integer saldoAtual = e.saldo;
System.out.println("saldo lido: " + saldoLido + " subtraído: "
+ r + " saldo atual: " + (saldoAtual) + " "
+ System.currentTimeMillis());
}
if(this.n.decrementAndGet() == 0)
this.saldos.remove("saldo");
}
}
}
public class Main {
public static void main(String[] args) throws Exception {
Map<String, Elem> saldos = new ConcurrentHashMap<>(20, 0.9f, 1);
AtomicInteger n = new AtomicInteger(0);
saldos.put("saldo", new Elem());
ExecutorService exec = Executors.newFixedThreadPool(20);
try {
for (int i = 0; i < 20; ++i)
exec.execute(new Sum(saldos, n));
exec.shutdown();
while (!exec.isTerminated()) {}
System.out.println("got elem: " + saldos.get("saldo") + " " + n);
} catch (Exception ex) {
exec.shutdownNow();
ex.printStackTrace();
}
}
}
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
public class Main
{
@Test
public void testExecute() throws Exception
{
int threadCount = 20;
final CountDownLatch threadsCompleteLatch = new CountDownLatch( threadCount );
Map<String, Elem> saldos = new ConcurrentHashMap<>( threadCount, 0.9f, 1 );
AtomicInteger counter = new AtomicInteger( 0 );
Elem element = new Elem();
saldos.put( "saldo", element );
ExecutorService exec = Executors.newFixedThreadPool( threadCount );
try
{
for ( int i = 0; i < threadCount; ++i )
{
exec.execute( new Sum( threadsCompleteLatch, counter, saldos ) );
}
threadsCompleteLatch.await();
exec.shutdown();
System.out.println( "got elem: " + saldos.get( "saldo" ) + " counter: " + counter );
System.out.println( "resulting element: " + element );
}
catch ( Exception ex )
{
exec.shutdownNow();
ex.printStackTrace();
}
}
}
class Elem
{
private final AtomicInteger saldo = new AtomicInteger( 1000 );
public int add( int value )
{
return saldo.getAndAdd( value );
}
int getSaldo()
{
return saldo.get();
}
@Override
public String toString()
{
return "Elem{ " +
"saldo=" + saldo.get() +
" }";
}
}
class Sum implements Runnable
{
private final Random rand = new Random();
private final CountDownLatch latch;
private final AtomicInteger counter;
private final Map<String, Elem> saldos;
Sum( CountDownLatch latch, AtomicInteger counter, Map<String, Elem> saldos )
{
this.latch = latch;
this.saldos = saldos;
this.counter = counter;
counter.incrementAndGet();
}
@Override
public void run()
{
int randomValue = rand.nextInt( 1000 );
Elem element = saldos.get( "saldo" );
if ( randomValue % 2 != 0 )
{
randomValue = -randomValue;
}
int saldoLido = element.add( randomValue );
int saldoAtual = element.getSaldo();
System.out.println(
"saldo lido: " + saldoLido + " somado: " + randomValue + " saldo atual: " + (saldoAtual) + " " + System.currentTimeMillis() );
if ( counter.decrementAndGet() == 0 )
{
saldos.remove( "saldo" );
}
latch.countDown();
}
}