日期:2014-05-20  浏览次数:20725 次

关于java集合线程安全的一个问题
Java code

package thread;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapTest
{
    Map<String, String> map = new ConcurrentHashMap<String, String>();

    /**
     * 添加
     */
    public void initMap()
    {
        for (int i = 0; i < 10000; i++)
        {
            map.put("" + i, "" + i);
        }
    }

    /**
     * 查询
     */
    public Map<String, String> getMap()
    {
        try
        {
            Thread.sleep(1);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return map;
    }

    public String getValue(String key)
    {
        try
        {
            Thread.sleep(1);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        return map.get(key);
    }

    public static void main(String[] args)
    {
        ConcurrentHashMapTest mapTest = new ConcurrentHashMapTest();
        // 初始化
        new TestThread(mapTest).start();
        // 查询
        // System.out.println(mapTest.getValue("9999"));
        // 查询
        System.out.println(mapTest.getMap().size());
    }
}

/**
 * 测试线程类
 */
class TestThread extends Thread
{
    ConcurrentHashMapTest mapTest = null;

    public TestThread(ConcurrentHashMapTest _mapTest)
    {
        this.mapTest = _mapTest;
    }

    /**
     * 
     */
    @Override
    public void run()
    {
        mapTest.initMap();
    }
}

class TestThread2 extends Thread
{
    ConcurrentHashMapTest mapTest = null;

    public TestThread2(ConcurrentHashMapTest _mapTest)
    {
        this.mapTest = _mapTest;
    }

    /**
     * 
     */
    @Override
    public void run()
    {
        mapTest.getMap();
    }
}


代码如上:这里有一个问题一直纠结我很久了,想问下.
这里用的线程安全的集合ConcurrentHashMap,按理说它提供的所有方法都是线程安全的,在操作ConcurrentHashMap不需要做其它线程安全的处理了。
但是在测试过程中发现假如有两个线程同时操作map,一个执行initMap操作,一个执行getMap,这个时候预期System.out.println(mapTest.getMap().size());这段代码会返回10000,但是执行的结果发现这个返回值会每次都不一样。

这样的话是不是需要在两个方法中将map加上同步代码块?我测试过加上同步代码块后
Java code

 /**
     * 添加
     */
    public void initMap()
    {
        synchronized (map)
        {
            for (int i = 0; i < 10000; i++)
            {
                map.put("" + i, "" + i);
            }
        }
    }

    /**
     * 查询
     */
    public Map<String, String> getMap()
    {
        try
        {
            // 让其它线程先持有map的锁
            Thread.sleep(1);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        synchronized (map)
        {
            return map;
        }
        
    }


System.out.println(mapTest.getMap().size());这段代码会始终返回10000.

综上所述:在多线程的环境中使用了线程安全的集合类型,是否还需要在方法中对集合进行同步?

P.S:附上jdk中对于线程安全集合(CopyOnWriteArraySet)的一个示例用法,请大鸟释疑,谢谢!

示例用法。 以下代码使用一个写时复制(copy-on-write)的 set,以维护在状态更新时执行某项操作的一组 Handler 对象。 
Java code

class Handler { void handle(); ... }

class X {
    private final CopyOnWriteArraySet<Handler> handlers = new CopyOnWriteArraySet<Handler>();
    public void addHandler(Handler h) { handlers.add(h); }

    private long internalState;
    private synchronized void changeState() { internalState = ...; }

    public void update() {
       changeState();
       for (Handler handler : handlers)
           handler.handle();
    }
 }


 

------解决方案--------------------
探讨

那我觉得容器应该就要做到我需求这个样的同步啊(按常理来说的话应该是集合没有初始化完成的时候是不能进行其他操作的),这样才是真正的安全啊。现在同步容器不能解决我的问题,貌似还需要我自己去同步map.不然还是线程不安全。