ThreadLocal-переменные

ThreadLocal-переменные

ThreadLocal - это тип поля, привязанный к конкретному потоку.
Это значит, что каждый поток имеет свой собственный, индивидуально инициализируемый экземпляр ThreadLocal-переменной, доступ к которой он получает через методы get() или set(). Даже если это поле будет статическое, оно все равно у каждого потока будет своё.

Как работает ThreadLocal? У каждого потока - т.е. экземпляра класса Thread - есть ассоциированная с ним таблица ThreadLocal-переменных. Ключами таблицы являются cсылки на объекты класса ThreadLocal, а значениями - ссылки на объекты, "захваченные" ThreadLocal-переменными.

Например, если мы объявим ThreadLocal-переменную:


ThreadLocal<Object> local = new ThreadLocal<Object>();
А затем, в потоке, сделаем:


local.set(myObject);
то ключом таблицы будет ссылка на объект local, а значением - ссылка на объект myObject. При этом для другого потока мы можем "положить" внутрь local другое значение.


class MyLogger {

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

    public void toLog(String message) {
        LocalDateTime date = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mm:ss:n");
        System.out.println(date.format(formatter) + " " + message);
    }


class MyTask extends Thread {

    MyLogger logger;
    int countTasks;
    String threadName;

    MyTask(MyLogger logger, int countTasks) {
        this.logger = logger;
        this.countTasks = countTasks;

    }

    @Override
    public void run() {
        threadName = Thread.currentThread().getName();
        for (int i = 1; i <= countTasks; i++) {
            doSomeUseful();
            String message = threadName + " done task #" + i;
            logger.toLog(message);
            MyLogger.countTasks.set(i);
        }
        System.out.println(threadName
                + ": count of tasks is "
                + MyLogger.countTasks.get());
    }

    private void doSomeUseful() {
        try {
            Thread.sleep((long) Math.random() * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace(System.out);
        }
    }
}


MyLogger logger = new MyLogger();
MyTask task1 = new MyTask(logger, 5);
MyTask task2 = new MyTask(logger, 3);
task1.start();
task2.start();
Возможный вывод:


/*
24:52:538000000 Thread-0 done task #1
24:52:538000000 Thread-1 done task #1
24:52:553000000 Thread-0 done task #2
24:52:553000000 Thread-1 done task #2
24:52:553000000 Thread-0 done task #3
24:52:553000000 Thread-1 done task #3
Thread-1: count of tasks is 3
24:52:553000000 Thread-0 done task #4
24:52:553000000 Thread-0 done task #5
Thread-0: count of tasks is 5
*/
Хорошая статья о ThreadLockal-переменных
Многопоточность