2013-02-08 3 views
3

Скажем, у меня есть класс:Как Guice впрыскивает одиночек и не-одиночек в несколько потоков

public class MyTask implements Runnable { 
    @Inject 
    private Fizz fizz; 

    // Getters and setters for 'fizz'. 

    @Override 
    public void run() { 
     if(fizz.alleviatesBuzz()) 
      doA(); 
     else 
      doB(); 
    } 

    private void doA() { ... } 

    private void doB() { ... } 
} 

А потом у меня есть еще один класс:

public class MyTaskDispatcher { 
    @Inject 
    private ThreadFactory threadFactory; 

    private Executor executor; 

    // Getter and setter for 'threadFactory'. 

    public void dispatch(MyTask task) { 
     if(executor == null) 
      executor = Executors.newCachedThreadPool(threadFactory); 

     executor.submit(task); 
    } 
} 

Так Guice впрыскивает MyTask с Fizz, и также вводит MyTaskDispatcher с ThreadFactory, который затем используется для создания и выполнения MyTask экземпляров, которые передаются. И, поскольку это кешированный пул, он создает новый поток только тогда, когда он нужен, но недоступен.

Мне интересно, как Guice «ведет себя» в многопоточной среде, когда мы вводим Fizz в качестве одноэлементного или неэлементного.

Давайте начнем с не-синглтон для-например:

public class MyAppModule extends AbstractModule { 
    @Override 
    public void configure() { 
     bind(Fizz.class).to(FizzImpl.class); 

     // I don't think the functionality of MyThreadFactory 
     // really matters for the sake of this question. 
     bind(ThreadFactory.class).to(MyThreadFactory.class); 
    } 

    @Provides 
    FizzImpl providesFizz() { 
     return new FizzImpl(true, Buzz.ALWAYS, 35); 
    } 

    // I *believe* we always want the ThreadFactory to be singleton, 
    // because all of the threads spawn from it and its executor. 
    @Provides @Singleton 
    ThreadFactory providesThreadFactory() { 
     return new MyThreadFactory(12); 
    } 
} 

Теперь скажем приложение уже работает на некоторое время, и 3 отдельных MyTask ей были представлены, и существуют, таким образом, 3 бегущие нити , Поскольку мы не просили Guice вводить Fizz es как singleton, я предполагаю, что каждый поток имеет свою собственную копию введенного FizzImpl, и нам не нужно добавлять какой-либо код synchronize, чтобы предотвратить столкновение 3 FizzImpl s и вызывая проблемы с потоком.

Но что происходит, когда мы делаем инъекцию Guice FizzImpl в качестве одноэлементного?!? Теперь в MyAppModule:

@Provides @Singleton 
    FizzImpl providesFizz() { 
     return new FizzImpl(true, Buzz.ALWAYS, 35); 
    } 

Если Guice только обеспечивает 1 глобальной, одноплодную экземпляр FizzImpl, какова нисходящие последствия этого FizzImpl «копировать» (если это правильное слово для этого) внутри каждый из 3 порожденных нити? Каковы подводные камни, на которые нужно следить? Каковы способы борьбы с этими подводными камнями? Заранее спасибо.

+5

Если вы вводите '@ Singleton', то у вас будет три ссылки на один и тот же объект. Я не уверен, что это удивительно или отличается от того, что произойдет, если вы не используете DI. –

+0

Спасибо @Louis Wasserman (+1) - так что в этом случае я должен был убедиться, что singleton 'FizzImpl' был потокобезопасным, да? – IAmYourFaja

+2

Абсолютно. Это справедливо для любого объекта, на который ссылаются несколько потоков. –

ответ

1

Нет, Fizz будет создан с экземпляром MyTask, и он будет сохраняться, используя несколько потоков вызовов. Если вы хотите иметь копию Fizz для каждого потока, вы должны делать это ленивым способом.

public class MyTask implements Runnable { 
    @Inject 
    private Provider<Fizz> fizzProvider; 

    // Getters and setters for 'fizz'. 

    @Override 
    public void run() { 
     Fizz fizz = fizzProvider.get(); 
     if(fizz.alleviatesBuzz()) 
      doA(); 
     else 
      doB(); 
    } 

    private void doA() { ... } 

    private void doB() { ... } 
} 

Как когда-либо, если вы поставите флаг Singleton для Fizz связывания, провайдер будет возвращать тот же экземпляр, если вы звоните fizzProvider.get(), так что все потоки будут иметь один и тот же экземпляр. Вы должны держать его не одиночным.

Также ваш модуль ошибочен, вы должны использовать метод или неявное связывание, а не оба. Также вы не можете предоставить экземпляр и ввести его интерфейс.

public class MyAppModule extends AbstractModule { 
    @Override 
    public void configure() { 
     bind(Fizz.class).to(FizzImpl.class); 
     //or bind(Fizz.class).toInstance(new FizzImpl(true, Buzz.ALWAYS, 35)); //Singleton!! 
     //or bind(Fizz.class).toProvider(new Provider<Fizz>() { 
     //  @Override 
     //  public Subject get() { 
     //  return new FizzImpl(true, Buzz.ALWAYS, 35); 
     //  } 
     // }); 

     // I don't think the functionality of MyThreadFactory 
     // really matters for the sake of this question. 
     bind(ThreadFactory.class).to(MyThreadFactory.class); 
    } 
} 

или

public class MyAppModule extends AbstractModule { 
    @Override 
    public void configure() { 
    } 

    @Provides 
    Fizz providesFizz() { 
     return new FizzImpl(true, Buzz.ALWAYS, 35); 
    } 

    // I *believe* we always want the ThreadFactory to be singleton, 
    // because all of the threads spawn from it and its executor. 
    @Provides @Singleton 
    ThreadFactory providesThreadFactory() { 
     return new MyThreadFactory(12); 
    } 
} 

Надеется, что это поможет!