在线程1中调用的构造函数,在线程2中专门访问的字段 - 是否需要volatile?

时间:2013-03-13 02:19:50

标签: java concurrency constructor volatile

我有一个由主线程实例化的类。然后这个类产生第二个线程,即处理线程。处理线程调用类的某些方法(处理方法),访问/更改字段。除了处理线程之外,其他任何方法和字段都不会被访问。但是,初始化它们的构造函数在主线程上运行。

该类扩展了一个通用的“协议”类,它包含输入处理线程,该线程调用处理接收消息的函数。最初,我在泛型类的构造函数中自动启动了处理线程,结果证明这是一个非常愚蠢的想法:

  1. 子类称为超级构造函数
  2. 超级构造函数启动了线程
  3. 线程立即使用空消息调用消息处理方法(使其在协议中发送第一条消息)。该方法设置了“已发送消息计数器”。
  4. 在主线程上,超级构造函数返回,子类初始化set消息计数器,将其重置为零。
  5. 我现在通过将处理线程的启动移动到另一个方法并在子类的构造函数的末尾调用它来改变它:

    public ProtocolSubclass() {
        super();
        startProcessingThread();
    }
    

    我假设当我调用startProcessingThreads()时,保证字段被初始化。调用startProcessingThread()后,只能从该线程访问该字段。 我可以这样做吗?我是否需要将字段标记为volatile,因为它在主线程上初始化但在处理线程上读取?

    我想我这次做对了,但经过几个小时的调试后,我宁愿问......

    根据要求,这里有更详细(仍然简化)的代码。请注意,上面的代码更加简化,因此可能与下面的代码不完全匹配。正在行动的领域是currentMsg:

    public abstract class ProtocolConnection {
        public ProtocolConnection(/*stuff*/) {
            /*stuff*/
            // DO NOT DO THIS HERE: startProcessingThreads();
        }
    
        protected void startProcessingThreads() {
            inputProcessingThread.start();
        }
    
        private final Thread inputProcessingThread = new Thread() {
            public void run() {
                if (isInitiator) initiateConnection();
                while (!closed && !finished) {
                    ProtocolMessage msg = new ProtocolMessage(inputStream);
                    log("received", Integer.toString(msg.tag), Integer.toString(msg.length));
                    ProtocolConnection.this.processMessage(msg);
                }
            }
        };
    }
    
    
    public class SimpleProtocolConnection extends ProtocolConnection {
        private int currentMsg = 0;
    
        public SimpleProtocolConnection(/*stuff*/) {
            super(/*stuff*/);
            startProcessingThreads();
        }
    
        @Override
        protected void processMessage(ProtocolMessage msg) {
            if (msg.tag != LAST_MESSAGE) {
                sendNext();
            }
        }
    
        @Override
        protected void initiateConnection() {
            sendNext();
        }
    
        private void sendNext() {
            addToSendingQueue(new ProtocolMessage(currentMsg, getData())); // very simplified
            currentMsg++;
        }
    
    }
    

2 个答案:

答案 0 :(得分:2)

该字段在线程1中初始化;然后启动线程2;然后线程2专门访问该字段。正确?如果是,那么......

不需要挥发性/原子。

基于JLS,在线程B开始之前在某个线程A中执行的操作对线程B可见。这以几种不同的方式陈述:

  

17.4.2。操作

     

线程间操作是由一个线程执行的操作   被另一个线程检测或直接影响。有   程序可以执行的几种线程间操作:

     

[...]

     

启动线程或检测线程已终止的操作   (§17.4.4)。

-

  

17.4.4。同步订单

     

每次执行都有一个同步顺序。同步订单   是对所有同步操作的总顺序   执行。对于每个线程t,该同步顺序   t中的同步动作(第17.4.2节)与程序一致   t(

)的顺序(第17.4.3节)      

同步动作会导致同步关系   行动,定义如下:

     

[...]

     

启动线程的操作与第一个操作同步   它开始的线程。

-

  

17.4.5。发生在订单之前

     

可以通过先发生关系来排序两个动作。如果一个   行动发生在另一个之前,然后第一个是可见的和   在第二天之前订购。

     

[...]

     

对线程的start()调用发生在 - 之前的任何操作之前   开始了。

答案 1 :(得分:-1)

volatile表示我将通过不同的线程修改特定字段。如果构造函数标记为synchronized,则不需要它,否则需要它。