类内部线程安全

时间:2013-09-19 15:05:00

标签: java multithreading thread-safety

我正在用Java编写一个内部多线程的类,因为它初始化并使用一个单独的线程来更新其私有字段。

class Foo {
    private volatile Byte channel = new Byte(0);
    private volatile Byte mode = new Byte(0);

    public Foo() {
        Thread t = new Thread(new UpdateFields());
        t.setDaemon(true);
        t.start();
    }

    public Byte getChannel() {
        return this.channel;
    }  

    public Byte getMode() {
        return this.mode;
    }

    private class UpdateFields implements Runnable {
        @Override public void run() {
            Byte data[];
            //get new data[]...
            channel = data[0];
            mode = data[1];
        }
    } 
}

我的问题是,这个类内部是线程安全的吗?从我读到的关于不可变对象(如Byte等)的内容来看,它们本质上是线程安全的。

编辑:向字段添加默认值

3 个答案:

答案 0 :(得分:7)

  

我的问题是,这个类内部是线程安全的吗?从我读到的关于不可变对象(如Byte等)的内容来看,它们本质上是线程安全的。

我在您的课程中看到的线程安全问题是您正在更新两个看起来相关的字段。由于竞争条件,可能会看到新的channel值和旧的mode值。我会使用volatile ChannelMode对象而不是您的两个volatile字段。

public class ChannelMode {
    private byte channel;
    private byte mode;
    public byte getChannel() {
       return channel;
    }
    public byte getMode() {
       return mode;
    }
}

虽然没有必要,但我希望直接使用Atomic*类而不是volatile,所以我会使用AtomicReference<ChannelMode>。所以你的代码看起来像是:

private AtomicReference<ChannelMode> channelModeRef =
    new AtomicReference<ChannelMode>(
        new ChannelMode(INITIAL_CHANNEL, INITIAL_MODE));
...
Byte data[];
// get new data[]...
// atomic operation to set the new channel-mode
channelModeRef.set(new ChannelMode(data[0], data[1]);

如果您的内部线程经常更新ChannelMode,那么您的类应该是线程安全的,因为值将以原子方式更新并且内存正确同步。

答案 1 :(得分:1)

如果更改内部字段,则该类不是不可变的。不可变类不会改变。这意味着您的类不是线程安全的。

如果您没有公开任何setter或任何其他更改类的方法,那么它应该是不可变的,但在这种情况下,您可以在内部更改它。 固有的线程安全意味着它不能被更改,所以你不必担心不同的线程会竞争和破坏彼此的数据。这肯定不是这种情况。

答案 2 :(得分:1)

你的班级不是线程安全的。 原因:如果//get new data[]...是一个耗时的步骤,那么其他方法可能会尝试在内部字段初始化之前读取内部字段。构造函数中的内部线程不会停止对象构造和初始化,因此在准备数据之前它可能具有不一致的状态。