Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我可以初始化静态成员一次并跨线程共享它吗?_Java_Multithreading_Concurrency_Static - Fatal编程技术网

Java 我可以初始化静态成员一次并跨线程共享它吗?

Java 我可以初始化静态成员一次并跨线程共享它吗?,java,multithreading,concurrency,static,Java,Multithreading,Concurrency,Static,这是我第一次尝试编写一个使用多线程的程序,因此我有几个关于在程序中使用并发的问题 我的程序从web UI获取用户输入,然后使用该用户输入启动一个进程。我知道我必须利用并发性,因为这个过程需要一个小时以上,而且我不可能让用户在开始下一个过程之前等待一个过程完成 下面的简化代码处理用户输入,然后启动流程 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletExcep

这是我第一次尝试编写一个使用多线程的程序,因此我有几个关于在程序中使用并发的问题

我的程序从web UI获取用户输入,然后使用该用户输入启动一个进程。我知道我必须利用并发性,因为这个过程需要一个小时以上,而且我不可能让用户在开始下一个过程之前等待一个过程完成

下面的简化代码处理用户输入,然后启动流程

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String myInput = request.getParameter("input");
    Thread t = new Thread(new MyRunnable(myInput));
    t.start();

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("Process started!");
    out.close();
}
下面的代码简化了我的实际过程

public class MyRunnable implements Runnable {

    private static HashMap<String,String> mapOfConstants = null;

    private String member;

    public MyRunnable(String member) {
        this.member = member;
    }

    @Override
    public void run() {
        if (mapOfConstants == null) init();
        // and so on...
    }

    private void init() {
        mapOfConstants = new HashMap<String,String>();
        mapOfConstants.put("LOCATION", "http://localhost/folder");
        // and so on...
    }

}
公共类MyRunnable实现Runnable{
私有静态HashMap mapOfConstants=null;
私有字符串成员;
公共MyRunnable(字符串成员){
this.member=成员;
}
@凌驾
公开募捐{
if(mapOfConstants==null)init();
//等等。。。
}
私有void init(){
mapOfConstants=newHashMap();
常量的映射。放置(“位置”http://localhost/folder");
//等等。。。
}
}
在上面的代码中,我打算将一系列占位符定义为一个常量,它将存储在HashMap
mapOfConstants

编辑: 最终,我可能希望使这个映射的初始化从其他地方获取值,比如文本文件


“我的代码”是否达到了在
MyRunnable
的所有实例之间共享此占位符映射的目的,只执行一次初始化过程?

如果您想在所有用户之间共享常量,那么您的路径是正确的,但是您必须
同步您的代码

同步代码的最简单方法是编写

public synchronized void run() {
}
请阅读一些关于Java同步的教程,因为这是Java中的一个雷区,即使是经验丰富的开发人员有时也会遇到问题


关于你的第二个问题:请写一个新问题。

我正在回答你的问题1,你应该为第2个问题发布另一个问题

我的代码是否达到共享此占位符地图的目的 在MyRunnable的所有实例中,执行此初始化过程 只有一次

是,但它不是线程安全的。所以你有两个选择:

我的回答假设您不想在任何时候更改地图内容 运行时,正如您所说,它是常量的映射

  • 选项1:使映射成为最终映射,并使用集合。不可修改映射
并在静态块中初始化它,这也使代码线程安全

  • 选项2:(同步)如果您想使用惰性初始化,这在这里显然不是必需的,那么您必须使代码线程安全您的代码不是线程安全的。

  • 原因:多个正在运行的线程可以将映射视为null并调用init,这将多次初始化映射。使用
    synchronized

    //keeping map `volatile`
    private static volatile HashMap<String,String> mapOfConstants = null;
    
    ...
    if(map == null)
      synchronized(SomeClass.class){
         if(map == null){
            init();
         }
      }
    
    //保持map`volatile`
    私有静态volatile HashMap mapOfConstants=null;
    ...
    if(map==null)
    已同步(SomeClass.class){
    if(map==null){
    init();
    }
    }
    
    首先,不要在
    run()
    方法中初始化映射,不能保证初始化只发生一次。当然,在这种情况下,不管你创建了多少次地图,最终一个会被设置为静态参考,其他的会被GCed。只是不太好看。我建议使用静态初始化块:

    private final static Map<String,String> mapOfConstants;
    static {
        Map<String, String> map = new HashMap<String, String>();
        // initialize map.
        map.put("", "");
        ...
    
        // convert the map into unmodifiable
        mapOfConstants = Collections.unmodifiableMap(map);
    }
    

    通过这种方式,您可以针对不同的常量配置编写测试,而无需修改可运行类。

    是的,您可以初始化静态成员一次,并在线程之间共享它,但前提是您必须格外小心。例如,必须同步所有init()调用站点;也就是说,init()只能从构造函数或同步方法调用。或者,您可以在静态初始值设定项块中初始化mapOfConstants变量。无论您使用哪种方法,您也可以将实现看作是<强> MAPOFMODENT/<强>变量的具体类型,因为这将避免以后出现头痛。

    如果不谨慎行事,可能会导致您遇到“双重检查锁定”竞争条件。此外,初始化静态引用变量通常被认为是一种反模式,因为在这种情况下,映射中的条目可以在程序的一次激活中不受限制地增长。通常,这种引用的唯一可接受的用法是,如果内容是恒定的,或者如果内容随着时间的推移增长非常缓慢(以对数为单位)。对数增长可以通过使用a来辅助,否则,可以明智地使用


    到目前为止,重点似乎是初始化静态mapOfConstants变量。但这忘记了地图的全部目的(通常)是存储一些东西,以便在最好的情况下在O(1)时间内检索。请记住,当存储和检索操作(在mapOfConstants变量上)跨越线程边界时,这些操作也必须同步。如果没有同步,一个线程的添加或编辑可能会被其他线程错过,这可能会影响程序的数据完整性。

    我知道这不会收到很好的效果,但您的代码几乎没有问题。尽管不是严格的线程安全(就只加载一次属性而言),但它仍然是正确的(就不创建损坏的数据而言)

    最大的变化是:

    private void init() {
        HashMap<String,String> tempMap = new HashMap<String,String>(); // <--- new object assigned to a placeholder variable
        tempMap.put("LOCATION", "http://localhost/folder");
        // and so on...
        mapOfConstants = Collections.unmodifiableMap(tempMap); // <--- atomic assignment here
    }
    
    private void init(){
    
    HashMap tempMap=new HashMap();//对于初学者,您可以使映射成为最终映射,并在声明时对其进行初始化。请不要在一个堆栈溢出问题中要求多个答案。请编辑您的问题以仅包含一个问题
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws     ServletException, IOException {
        Map<String, String> sharedMapOfConstants = MapOfConstantsFactory.getMapOfConstants();
        String myInput = request.getParameter("input");
        Thread t = new Thread(new MyRunnable(myInput, sharedMapOfConstants));
        t.start();
    
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Process started!");
        out.close();
    }
    
    private void init() {
        HashMap<String,String> tempMap = new HashMap<String,String>(); // <--- new object assigned to a placeholder variable
        tempMap.put("LOCATION", "http://localhost/folder");
        // and so on...
        mapOfConstants = Collections.unmodifiableMap(tempMap); // <--- atomic assignment here
    }