final hashmap是线程安全的

时间:2016-12-27 09:35:39

标签: java multithreading synchronization thread-safety final

public class MapDem {

 final HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();

 public HashMap<Integer,Integer> getMap(){
     return map;
 }
 public void putValue(int key,int value){
     map.put(key,value);
 }

 public static void main(String args[]){
    MapDem demo = new MapDem();
    new Thread(new Runnable(){

        @Override
        public void run() {
                demo.putValue(1, 10);

        }

    }).start();

    new Thread(new Runnable(){

        @Override
        public void run() {
            demo.putValue(1, 10);

        }

    }).start();

System.out.println(demo.getMap().size());

}

}

final字段本身就是线程安全的吗?在上面的代码中,map变量标记为final,这是否意味着它是线程安全的?

如果变量线程安全,我希望main - 方法的输出应为“2”,但我得到“1”或“0”

修改

如果我使用volatile关键字声明变量,即

volatile HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();

map变量看似不是线程安全的,为什么会这样?但是下面的方法似乎有效,为什么会这样?

public  synchronized void putValue(int key,int value){
    if(map.isEmpty()){
        System.out.println("hello");
        map.put(key,value);     
}

使用Collections.unmodifiableMap(map)会有效吗?

4 个答案:

答案 0 :(得分:1)

你的测试有问题。如果两个value存储有相同的keyHashMap.put(K key, V value) will overwrite the former value with the later。因此,即使没有并发性,您的&#34;测试&#34;将返回1的大小。

代码:

import java.util.HashMap;

public class MapDem {

    final HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();

    public HashMap<Integer, Integer> getMap() {
        return map;
    }

    public void putValue(int key, int value) {
        map.put(key, value);
    }

    public static void main(String args[]) {
        MapDem demo = new MapDem();

        demo.putValue(1, 10);
        demo.putValue(1, 10);

        System.out.println(demo.getMap().size());
    }
}

输出(Ideone demo):

1

有时可以看到0大小的事实是由于缺少阻塞结构。在通过调用join() on your Thread-objects查询您的地图大小之前,您应该等待两个Thread的完成。

            Thread t1 = new Thread(new Runnable() {

                @Override
                public void run() {
                    demo.putValue(1, 10);

                }

            });
            t1.start();

            Thread t2 = new Thread(new Runnable() {

                @Override
                public void run() {
                    demo.putValue(1, 10);

                }

            });
            t2.start();

            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(demo.getMap().size());

As mentioned by @SachinSarawgifinal不会使您的代码符合线程安全且as further explained by @assyliasvolatile在这种情况下不会删除它。

如果您需要线程安全的Hashmap,请使用ConcurrentHashMap

如果你决定编写Map interface自己的线程安全实现,我建议Oracle's Lesson on Concurrency开始,然后是Brian Goetz's "Java Concurrency in Practice",可能还有一点Javier Fernández González' "Mastering Concurrency Programming with Java 8" }}

答案 1 :(得分:1)

您问题的直接答案是:不,最终关键字不会使字段线程安全

该关键字仅告知编译器必须确保为该字段分配了一个值(不是零或多个赋值)。

您知道,获取多线程代码 正确被认为是难以的原因。

正确的多线程的本质是:当某个状态可以被一个线程更新,但被其他线程使用(或更新)时,以确保只获得那些状态您希望看到的更改。

长话短说:你有很多学习要做;一个好的起点是here

答案 2 :(得分:0)

什么是线程安全的是对Camera.main.aspect = 1F; //for example, a square area 变量的访问:读取该变量的所有线程都将看到相同的对象引用。

然而,HashMap(get / put)上的操作不是线程安全的,这与map是最终的事实无关。

因此,除非您在map方法周围添加一些并发控制,否则您的代码不是线程安全的 - 例如将其设为putValue

答案 3 :(得分:0)

使参考变量最终确保引用变量不能更改它已分配给它的对象引用。

但是对象的价值可能会发生变化。同样的事情发生在这里你的对象值可能会改变。现在需要同步更改值的代码。 您可以使用ConcurrentHashMap摆脱所有同步问题。你可以阅读它here

使用ConcurrentHashMap确保对象的每个写操作都应该由一个线程一次处理。它也优化了HashMap的读数。它将HashMap分成块,不同的线程可以从不同的块中读取。

相关问题