Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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_Thread Safety_Immutability_Safe Publication - Fatal编程技术网

通过静态工厂确保java中的安全发布和线程安全

通过静态工厂确保java中的安全发布和线程安全,java,multithreading,thread-safety,immutability,safe-publication,Java,Multithreading,Thread Safety,Immutability,Safe Publication,下面的类是不可变的(但请参见编辑): 公共最终类位置扩展数据{ 双纬度; 双经度; 字符串提供者; 私有位置(){} 私有静态枚举位置字段实现 田地{ 拉特{ @凌驾 公共列表获取数据(位置loc,最终位置out){ 最终双纬度=loc.getLatitude(); out.纬度=纬度; //返回数组列表 } @凌驾 公共无效解析(列表、最终位置pos) 抛出解析异常{ 试一试{ 位置纬度=listToDouble(列表); }捕获(数字格式){ 抛出新的ParserException(“格式错

下面的类是不可变的(但请参见编辑):

公共最终类位置扩展数据{
双纬度;
双经度;
字符串提供者;
私有位置(){}
私有静态枚举位置字段实现
田地{
拉特{
@凌驾
公共列表获取数据(位置loc,最终位置out){
最终双纬度=loc.getLatitude();
out.纬度=纬度;
//返回数组列表
}
@凌驾
公共无效解析(列表、最终位置pos)
抛出解析异常{
试一试{
位置纬度=listToDouble(列表);
}捕获(数字格式){
抛出新的ParserException(“格式错误的文件”,e);
}
}
}/*,长,提供程序,时间(来自数据超类的字段)*/;
}
// ========================================================================
//静态API(基本上是工厂)
// ========================================================================
公共静态位置保存数据(上下文ctx、位置数据)
抛出IOException{
最终位置输出=新位置();
最终列表listByteArrays=new ArrayList();
对于(LocationFields bs:LocationFields.values()){
add(bs.getData(data,out.get(0));
}
保存数据(ctx、文件前缀、ListByteArray);
返回;
}
公共静态列表解析(文件f)引发IOException,
解析异常{
列出条目;
//从f填充条目
最终列表数据=新的ArrayList();
用于(枚举映射枚举映射:条目){
位置p=新位置();
for(LocationFields字段:enumMap.keySet()){
parse(enumMap.get(field),p);
}
数据。添加(p);
}
返回数据;
}
/**
*从给定字符串构造位置实例。完整副本
*粘贴只是为了得到图片
*/
来自字符串的公共静态位置(字符串s){
如果(s==null | | s.trim().equals(“”)返回null;
最终位置p=新位置();
String[]split=s.split(N);
p、 时间=Long.valueOf(分割[0]);
int i=0;
p、 经度=Double.valueOf(split[++i].split(IS)[1].trim());
p、 纬度=Double.valueOf(split[++i].split(IS)[1].trim());
p、 provider=split[++i]。split(IS)[1]。trim();
返回p;
}
}
由于它是不可变的,所以它也是线程安全的。正如您所看到的,构造此类实例的唯一方法(反射除外,反射实际上是另一个问题)是使用提供的静态工厂

问题

  • 是否存在此类的对象可能被不安全地发布的情况
  • 是否存在返回的对象是线程不安全的情况
编辑:请不要评论非私有字段-我意识到这不是字典中的一个不变类,但包在我的控制下,我永远不会手动更改字段的值(在构造ofc之后)。没有提供突变子

另一方面,非最终字段是问题的要点。当然,我意识到,如果它们是最终的,那么类将是真正不可变的和线程安全的(至少在Java5之后)。不过,如果能提供一个在这种情况下使用不当的例子,我将不胜感激


最后,我并不是说静态工厂与线程安全有任何关系,正如一些评论所暗示的那样。重要的是,创建此类实例的唯一方法是通过这些(当然是静态的)工厂。

类的
位置的字段不是
最终的
,因此我相信构造函数不会安全地发布它们的值。因此,构造函数不是线程安全的,因此使用它们的代码(例如工厂方法)不会生成线程安全的对象。

类的
位置的字段不是
最终的
,因此我相信构造函数不会安全地发布它们的值。因此,构造函数不是线程安全的,因此使用它们的代码(例如工厂方法)不会生成线程安全对象。

位置不是不可变的,字段具有包可见性并且不是最终的,请参见此处不可变类的定义:

此外,由于字段不是最终字段,并且没有其他机制来确保安全发布,因此位置未安全发布。安全出版的概念在许多地方都有解释,但这一点似乎特别相关: 也有相关的消息来源

简而言之,安全发布是关于当您将构造实例的引用提供给另一个线程时会发生什么,该线程是否会按预期查看字段值?这里的答案是否定的,因为Java编译器和JIT编译器可以自由地对引用发布的字段初始化重新排序,从而导致其他线程可以看到未完成的状态

最后一点很关键,从OP评论到下面的一个答案,他似乎认为静态方法的工作方式与其他方法不同,但事实并非如此。静态方法可以像任何其他方法一样内联,构造函数也是如此(Java 1.5之后的构造函数中的final字段除外)。需要明确的是,虽然JMM不能保证构建是安全的,但它在某些甚至所有JVM上都可以很好地工作。有关详细讨论、示例和行业专家意见,请参见并发兴趣邮件列表上的讨论:

底线是,它可能有效,但确实有效
public final class Position extends Data {

    double latitude;
    double longitude;
    String provider;

    private Position() {}

    private static enum LocationFields implements
            Fields<Location, Position, List<Byte>> {
        LAT {

            @Override
            public List<byte[]> getData(Location loc, final Position out) {
                final double lat = loc.getLatitude();
                out.latitude = lat;
                // return an arrayList
            }

            @Override
            public void parse(List<Byte> list, final Position pos)
                    throws ParserException {
                try {
                    pos.latitude = listToDouble(list);
                } catch (NumberFormatException e) {
                    throw new ParserException("Malformed file", e);
                }
            }
        }/* , LONG, PROVIDER, TIME (field from Data superclass)*/;

    }

    // ========================================================================
    // Static API (factories essentially)
    // ========================================================================
    public static  Position saveData(Context ctx, Location data) 
            throws IOException {
        final Position out = new Position();
        final List<byte[]> listByteArrays = new ArrayList<byte[]>();
        for (LocationFields bs : LocationFields.values()) {
            listByteArrays.add(bs.getData(data, out).get(0));
        }
        Persist.saveData(ctx, FILE_PREFIX, listByteArrays);
        return out;
    }

    public static List<Position> parse(File f) throws IOException,
            ParserException {
        List<EnumMap<LocationFields, List<Byte>>> entries;
        // populate entries from f
        final List<Position> data = new ArrayList<Position>();
        for (EnumMap<LocationFields, List<Byte>> enumMap : entries) {
            Position p = new Position();
            for (LocationFields field : enumMap.keySet()) {
                field.parse(enumMap.get(field), p);
            }
            data.add(p);
        }
        return data;
    }

    /**
     * Constructs a Position instance from the given string. Complete copy 
     * paste just to get the picture
     */
    public static Position fromString(String s) {
        if (s == null || s.trim().equals("")) return null;
        final Position p = new Position();
        String[] split = s.split(N);
        p.time = Long.valueOf(split[0]);
        int i = 0;
        p.longitude = Double.valueOf(split[++i].split(IS)[1].trim());
        p.latitude = Double.valueOf(split[++i].split(IS)[1].trim());
        p.provider = split[++i].split(IS)[1].trim();
        return p;
    }
}
class Option {

  private boolean value;

  Option(boolean value) { this.value = value; }

  boolean get() { return value; }

}