2015-07-05 4 views
7

У меня есть поле int x, к которому, как предполагается, можно получить доступ из многих потоков одновременно. Я хочу, чтобы x имел отдельную копию на поток, каждый из которых начинался с первоначального значения. Я попытался сделать это, используя ключевое слово volatile, но каждый новый поток по-прежнему изменяет x для других потоков.Как сохранить состояние для каждой нити отдельно

Вот пример кода:

public class StackOverflowThread0 { 

    public StackOverflowThread0() { 
     new A().start(); 
    } 

    public static void main(String[] args) { 
     new StackOverflowThread0(); 
    } 

    volatile int x = 0;//<--keep original for each thread? 

    class A extends Thread { 

     @Override 
     public void run() { 
      try { 
       while (true) { 
        getT().start(); 

        Thread.sleep(1000); 
       } 
      } catch (InterruptedException ex) { 
       ex.printStackTrace(); 
      } 
     } 
    } 

    Thread getT() { 
     Thread t = new Thread() { 

      @Override 
      public void run() { 
       x++; 

       for (int i = 0; i < 100; i++) { 
        try { 
         System.out.println("x=" + x + " | thread id=" + Thread.currentThread().getId() + "| 100*x+x=" + (100 * x + x)); 
         Thread.sleep(100); 
        } catch (InterruptedException ex) { 
         ex.printStackTrace(); 
        } 
       } 
       interrupt(); 
      } 
     }; 
     return t; 
    } 
} 

выход:

x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 
x=1 | thread id=10| 100*x+x=101 //thread 10 has x field value as 1 
x=2 | thread id=11| 100*x+x=202 //thread 11 modifies x field to 2 
x=2 | thread id=10| 100*x+x=202 //thread 10 lost x value as 1 :(

etc... 

Как сохранить отдельный x значение для каждого потока, или есть более оптимальный способ решить задача?

+4

Посмотрите здесь: http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html –

+0

@TotZam да, я пробовал атомарно, но кажется, что он не работает в случай, который я описываю; В любом случае я попробую еще раз немного :) – user390525

+0

@TotZam Я думаю, что он был отдельным значением для каждого потока, а не общим значением для всех потоков. –

ответ

4

Класс ThreadLocal может сделать это для вас. Он поддерживает переменную, которая отличается для каждого потока, который обращается к нему. Другими словами, каждый поток получает свою уникальную копию переменной.

Заканчивать следующие изменения в код:

//Initialisation of the variable. Here an initial value of zero is assigned. 
ThreadLocal<Integer> x = ThreadLocal.withInitial(()-> 0); 

//Incrementing the value by one: 
x.set(x.get() + 1); 

//Outputting the result: 
System.out.println("x=" + x.get() + " | thread id=" 
          + Thread.currentThread().getId() + "| 100*x+x=" + (100 * x.get() + x.get())); 

Edit: Для тех, кто использует 1.7, действительно ли способ использовать ThreadLocal без лямбда-выражения. Однако вам придется переопределить метод initialValue().

ThreadLocal<Integer> x = new ThreadLocal<Integer>() { 
    @Override 
    protected Integer initialValue() { 
     return 0; 
    } 
}; 
+0

Есть ли способ добиться того же результата, избегая лямбда-выражений? Я использую 1.7 прямо сейчас – user390525

+0

Does ThreadLocal означает, что поток 10 и поток 11, например, могут иметь собственное значение x как 1, поэтому значение x может дублироваться или нет? Дайте более подробную информацию – user390525

+0

Каждый раз, когда новый поток пытается получить доступ к переменной через threadLocal.get(), новый экземпляр Integer автоматически создается с использованием метода initialValue(). Этот экземпляр Integer принадлежит одному потоку и невидим для всех других потоков. Надеюсь, это объяснит это. –

3

Альтернатива Thread Local Storage, вы можете сохранить вашу переменную х на вашем стеке вызовов. Конечно, это зависит от того, как вы используете x; вам может понадобиться ваша переменная в куче. И в этом случае вам лучше работать с ThreadLocal.

Темы разделяют кучу, но have their own stack.

Это означает, что любые переменные, хранящиеся в стеке, сразу же являются локальными.

+0

Хороший, +1 для мышления вне коробки. –

+0

Я тоже так думал, но это довольно иррационально :) В качестве временного решения я попытался интегрировать int mX = x в каждый метод overriden run(); Но, да, ThreadLocal, кажется, является стандартным способом решения проблемы, если «x нормально дублировать», – user390525

Смежные вопросы