![](/img/trans.png)
[英]How can I perform a thread-safe get then remove with ConcurrentHashMap?
[英]How Can I remove an item from ConcurrentHashMap when no have a thread holding certain object anymore?
假設我有一個臨時集合(ConcurrentHashMap)來保存某個對象(例如HttpSession)的引用。 當至少一個線程正在使用會話時(在請求時),無法將其刪除。 但是,當沒有更多線程同時使用會話時,應將其刪除以釋放內存。 我嘗試實現類似於該示例的示例,但出現了NullPointerException。 我做錯了什么? :(
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();
}
}
}
我為您整理了一個可行的示例,可以幫助您解決問題。 我對Main進行了junit測試,以使其易於在您喜歡的IDE或其他地方運行。
需要注意的幾點。
添加了CountDownLatch,以便在關閉執行程序服務並打印出結果之前,所有線程都將完成。
Elem使用AtomicInteger,因此不再需要同步塊。
該代碼最重要的修復方法是在Sum類的構造函數中增加計數器,以便在每個線程都有機會處理之前,不會從映射中刪除Elem。 否則,一個線程可能會一直運行並刪除Elem,然后其他線程才有機會執行。
-帕特里克
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();
}
}
將其全部扔掉,然后使用java.util.WeakHashMap.
它已經完全可以滿足您的需求。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.