最终的 hashmap 是线程安全的

final hashmap are thread safe

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) 行得通吗?

线程安全的是对 map 变量的访问:读取该变量的所有线程都将看到相同的对象引用。

然而,HashMap (get/put) 上的操作不是线程安全的,这与 map 是否为 final 无关。

所以你的代码不是线程安全的,除非你在 putValue 方法周围添加一些并发控制 - 例如通过使它成为 synchronized.

将引用变量设置为 final 确保引用变量不能更改已分配给它的对象引用。

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

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

你的测试有问题。如果两个value存储了相同的keyHashMap.put(K key, V value) will overwrite the former value with the later。因此,即使没有并发,您的 "test" 也会 return 大小为 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());

, final does not make your code thread-safe and ,volatile在这种情况下不剪切。

如果你需要一个线程安全的Hashmap,使用ConcurrentHashMap

如果您决心编写自己的Map interface, I recommend Oracle's Lesson on Concurrency to start with, followed by Brian Goetz's "Java Concurrency in Practice" and maybe a little bit of Javier Fernández González' "Mastering Concurrency Programming with Java 8"线程安全实现。

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

该关键字仅告诉编译器它必须确保恰好有 一个 值分配给该字段(不是零或多个分配)。

你知道,获取多线程代码 正确被认为困难[=28=是有原因的].

正确的多线程的本质是:当一些状态可以被一个线程更新,但被其他线程使用(或更新)..以确保你只会得到你想看到的那些状态变化。

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