3

我正在用 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];
        }
    } 
}

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

编辑:向字段添加默认值

4

3 回答 3

7

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

我在您的课程中看到的线程安全问题是您正在更新两个看起来相关的字段。由于竞争条件,可能会看到一个新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经常更新,那么您的类应该是线程安全的,因为值将被原子更新并且内存正确同步。

于 2013-09-19T15:10:30.720 回答
1

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

确实,如果您不公开任何设置器或任何其他更改您的类的方法,那么它应该是不可变的,但在这种情况下,您需要在内部进行更改。固有的线程安全意味着它不能被更改,因此您不必担心不同的线程会竞争并破坏彼此的数据。这当然不是这里的情况。

于 2013-09-19T15:06:50.377 回答
1

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

于 2013-09-19T15:18:21.967 回答