В Java, возможно ли Синглтон на поток?

Используя потоки, у меня есть основной класс (SlaveCrawler), который создает три класса (Downloader, ContentAnalyzer, URLAnalyzer), которые зависят друг от друга.

SlaveCrawler использует Downloader и URLAnalyzer

Downloader использует ContentAnalyzer и URLAnalyzer

ContentAnalyzer использует URLAnalyzer

Мне нужен только один экземпляр каждого класса. Если я использую Singleton, я могу получить это, но работая с потоками, у меня будет 20 SlaveCrawlers (пример), поэтому я хочу 20 URLAnalyzer.

Возможно ли это сделать с помощью Singleton или мне нужен другой способ?

Ответы

Ответ 1

Взгляните на ThreadLocal. Каждый поток будет иметь свою локальную копию каждого объекта.

ThreadLocal<YourObject> threadLocalYourObject = new ThreadLocal<YourObject>() {
  @Override
  protected YourObject initialValue() {
    //initialize YourObject
  }
}

Или в 1.8 мы можем использовать:

ThreadLocal<YourObject> threadLocalYourObject = ThreadLocal.withInitial( () -> new YourObject() )

Чтобы получить доступ к объекту ThreadLocal, используйте метод get().

YourObject yourObject = threadLocalYourObject.get();

Ответ 2

Вы можете реализовать его с помощью ThreadLocal. Вот псевдокод:

public class ThreadLocalTest {

  public static void main(String[] args){
    MyTLSingleTon obj = MyTLSingleTon.getInstance();
  }

}

class MyTLSingleTon{

  private MyTLSingleTon(){  
  }

  private static final ThreadLocal<MyTLSingleTon> _localStorage = new ThreadLocal<MyTLSingleTon>(){
    protected MyTLSingleTon initialValue() {
      return new MyTLSingleTon();
   }
  };

  public static MyTLSingleTon getInstance(){
    return _localStorage.get();
  }
}

MyTLSingleTon.getInstance(); метод возвращает объект, ассоциированный с текущим потоком. и если объект не ассоциируется, чем будет вызван метод protected MyTLSingleTon initialValue(), и будет установлен новый экземпляр.

Ответ 3

Да. Это возможно.

В методе /, который возвращает одиночный элемент (например, getInstance()), используйте локальное хранилище потоков, чтобы проверить (и создать при необходимости) экземпляр объекта singleton для возврата.

Однако это звучит довольно грязно. Подумайте, просто не используя для этого "singleton pattern"; а скорее передать тот же объект по мере необходимости (или рассмотреть возможность использования рамки DI).

Счастливое кодирование.

Ответ 4

I have used the implementation as explained above, I am able to achieve the single object per thread.
Below is my complete implementation

package example.test;

public class ThreadLevelSingleton
{
    public static void main(String[] args)
    {
        Thread33 t3 = new Thread33();
        t3.run();

        Thread44 t4 = new Thread44();
        t4.run();

        MySingleTon m1 = MySingleTon.getInstance();
        MySingleTon m2 = MySingleTon.getInstance();

        System.out.println(Thread.currentThread().getName() + " : " + (m1 == m2));

        MySingleTon tm1 = t3.getMySingleTon();
        MySingleTon tm2 = t4.getMySingleTon();
        System.out.println(Thread.currentThread().getName() + " : " + (tm1.equals(tm2)));
    }
}

class MySingleTon
{
    private MySingleTon()
    {
    }

    private static final ThreadLocal<MySingleTon> t = new ThreadLocal<MySingleTon>()
    {
        @Override
        protected MySingleTon initialValue()
        {
            return new MySingleTon();
        }
    };

    public static MySingleTon getInstance()
    {
        return t.get();
    }
}

class Thread33 extends Thread
{
    MySingleTon m1;

    @Override
    public void run()
    {
        MySingleTon t = MySingleTon.getInstance();
        MySingleTon t1 = MySingleTon.getInstance();
        m1 = MySingleTon.getInstance();
        System.out.println(getName() + " : " + (t == t1));
        System.out.println(t);
        System.out.println(t1);
    }

    MySingleTon getMySingleTon()
    {
        return m1;
    }
}

class Thread44 extends Thread
{
    MySingleTon m1;

    @Override
    public void run()
    {
        MySingleTon t = MySingleTon.getInstance();
        MySingleTon t1 = MySingleTon.getInstance();
        m1 = MySingleTon.getInstance();
        System.out.println(getName() + " : " + (t == t1));
        System.out.println(t);
        System.out.println(t1);
    }

    MySingleTon getMySingleTon()
    {
        return m1;
    }
}