【问题标题】:How to make my code thread safe?如何使我的代码线程安全?
【发布时间】:2015-01-22 09:50:06
【问题描述】:

给定一个带有静态方法的Factory 类为SomeModel 类创建实例,它将在多线程环境中运行。这个Factory有三个方法:

  1. 开始
  2. 停止
  3. createSomeModel

还有一个state 字段,位于:

  1. 已关闭
  2. 开始
  3. starting_failed
  4. 开始
  5. 关闭
  6. shutting_failed

这是Factory 类的当前设计:

public class Factory {

    enum State{
        shutted, starting, starting_failed, started, shutting, shutting_failed
    }

    State state;

    public static void start(){
        // can only be invoked when state=shutted
    }

    public static void stop(){
        // can only be invoked when state=started
    }

    public static void restart(){
        stop();
        start();
    }

    public static SomeModel create(){
        // can only be invoked when state=started
    }
}

我的要求是:create方法只能在state==started时调用,start方法只能在state==shutted || state == shutting_failed时调用,stop方法只能在state==started|| state == starting_failed时调用。

我知道这与线程同步问题有关,但我对自己的线程知识没有信心。请帮帮我。

【问题讨论】:

  • 使用synchronized 块。
  • 你确定Factory 需要有static 方法吗?看起来您希望在所有应用程序中都拥有一个 Factory 实例,并且客户端无需调用 start 方法。
  • @LuiggiMendoza 是的,你是对的,使用静态方法是不是很糟糕?我应该使用单例模式吗?
  • 拥有static 方法还不错,但start 并不意味着是static。你可以让你的 Factory 成为一个枚举,它将是一个单例,它的初始化将是线程安全的。不过,您应该应用一些技术,以便方法的执行是线程安全的。由于您没有提供有关您的方法的具体细节,我们只能声明您应该在验证 Factory 的状态后使用 synchronized 块来实现 startstop 方法,以及 create方法根本不需要任何同步,除非它修改了Factory的状态。
  • 您将使用if 来检查状态,如果status 仍然是无效状态,则抛出RuntimeException 说明Factory 尚未初始化.

标签: java multithreading thread-safety


【解决方案1】:

我建议你根本不要使用静态方法。

相反,创建Factory 类的对象并让所有方法为synchronized

public enum State{
    shutted, starting, starting_failed, started, shutting, shutting_failed;
}

public class Factory {
    private State state;

    public synchronized void start(){
        if(state != State.shutted) {
            throw new RuntimeException("Can only be invoked when state=shutted");
        }
        // do task
    }

    public synchronized void stop(){
        if(state != State.started) {
            throw new RuntimeException("Can only be invoked when state=started");
        }
        // do task
    }

    public synchronized void restart(){
        stop();
        start();
    }
}

希望这会有所帮助。

【讨论】:

  • 但是“创建”方法怎么样?同步此方法会导致性能下降。
【解决方案2】:

无需同步。对状态使用 volatile 或 AtomicReference。 我正在给出使用 volatile 的示例。最好与原始类型一起使用,因此必须为不同的状态添加 int 值。虽然您可以使用枚举的序数,但这样就有点清楚了。否则,您可以使用 AtomicReference。

public class Factory {
    private static volatile int factoryState;

    public synchronized void updateFactoryState(State newState, State ... expectedStates){
        for (State state : expectedStates)
            if(factoryState == State.shutted.getStateVal()){
                factoryState = newState.getStateVal();
        }
    }

    public void start(){
        try{
            updateFactoryState(State.starting, State.shutted, State.shutting_failed);
            System.out.println("steps to start the factory");
            //someExpensiveStartupMethod();
        }catch (Exception e){
            updateFactoryState(State.starting_failed, State.starting);
        }
        updateFactoryState(State.started, State.starting);
    }

    public void stop(){
        try{
            updateFactoryState(State.shutting, State.started, State.starting_failed);
            System.out.println("steps to stop the factory");
            //someExpensiveStopFactoryMethod();
        }catch (Exception e){
            updateFactoryState(State.shutting_failed, State.shutting);
        }
        updateFactoryState(State.shutted, State.shutting);
    }

    public void restart(){
        stop();
        start();
    }
    public static SomeModel create(){
        if(factoryState == State.started.getStateVal()) {
            System.out.println("Create Model");
        }   else{
            throw new RuntimeException("Can not create Model.Factory not in started state.");
        }
        return null;
    }

    enum State{
        shutted(0), starting(1), starting_failed(2), started(3), shutting(4), shutting_failed(5);
        private final int stateVal;

        State(int i) {
            stateVal = i;
        }

        public int getStateVal() {
            return stateVal;
        }
    }

    class SomeModel {}
}

【讨论】:

  • 这个实现被破坏了,因为 start()、stop()、create() 包含竞争条件。一般来说,volatile 仅适用于如果您想读取共享值而不更改它,或者如果您想原子地更改原始值并且不必关心它的当前值。
  • 请多多关照,请再次阅读有关 volatile 的内容。最高级别的线程安全来自于不变性。您既不需要任何安全措施来读取不可变变量(如 volatile 或任何),也不需要 volatile 为您提供对所有原始类型的原子操作。请再次访问 volatile。
  • 该代码中的问题:操作 a) if(factoryState==...);操作 b) factoryState=nextState 。如果您的线程在 a) 之后但在 b) 之前暂停,则 factoryState 可能会在您的线程继续其工作之前收到意外的值。这是一个竞争条件。
  • 同意比赛条件 :) 但仍不同意声明的其他部分并支持上述评论。
【解决方案3】:

这个方法不同步 create 方法,所以这不会是一个瓶颈。当你停下来时,不要让执行新的创建。 双重检查“if state == started”对于避免竞争条件是必要的。

可能有比使用 Thread.sleep 等待所有创建模型完成更好的解决方案,但我看不出如何轻松完成。

希望对你有帮助。

enum State{
    shutted, starting, starting_failed, started, shutting, shutting_failed
}

private Factory() {
    // singleton: no more than 1 instances allowed
}

public static Factory getInstance() {
    return instance;
}

private static final Factory instance = new Factory();
private final AtomicInteger threadsCreatingModel = new AtomicInteger();
private volatile State state;


public synchronized void start(){
    if(state != State.shutted) {
        throw new RuntimeException("Can only be invoked when state=shutted");
    }
    state = State.starting;
    // TODO: task
}

public synchronized void stop() throws InterruptedException {
    if(state != State.started) {
        throw new RuntimeException("Can only be invoked when state=started");
    }
    state = State.shutting;

    // wait all threads that are creating SomeModel
    while (threadsCreatingModel.intValue() > 0) {
        Thread.sleep(500);
    }
    // TODO: task
}

public SomeModel create(){
    if(state == State.started) {
        threadsCreatingModel.incrementAndGet();
        if(state == State.started) {
            // TODO: task   
        }
        threadsCreatingModel.decrementAndGet();
    }
}

【讨论】:

  • 但是“创建”方法怎么样?同步此方法会导致性能下降。
  • 我已经更改了创建方法不需要同步的代码,并且为了停止等待所有创建方法完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-08
  • 2020-12-10
  • 1970-01-01
相关资源
最近更新 更多