簡體   English   中英

當沒有線程保存某些對象時,如何從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM