使用ThreadLocal为每个线程分配ID

时间:2017-06-02 22:49:37

标签: java multithreading threadpool threadpoolexecutor

在下面的程序中,我想为每个线程分配不同的id,但是在输出中,每个线程都有不一致的id,如输出中所示。但是,如果我取消注释system.out语句,则会为每个线程分配唯一的ID,但不确定原因。

class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException, 
ExecutionException {
    CustomerThread custThread1 = new CustomerThread("Sampath");
    CustomerThread custThread2 = new CustomerThread("Harish");
    CustomerThread custThread3 = new CustomerThread("Harsha");
    CustomerThread custThread4 = new CustomerThread("Gowtham");
    custThread1.start();
    custThread2.start();
    custThread3.start();
    custThread4.start();
    }
}

class CustomerThread extends Thread {
static Integer custId = 0;
private  static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
        //System.out.println("will work");
        return ++custId;
    }
};

CustomerThread(String name) {
    super(name);
}

public void run() {
    System.out.println(Thread.currentThread().getName() + " executing with id: " + tl.get());
}
}

输出是:

Sampath executing with id: 1
Harish executing with id: 
Harsha executing with id: 2
Gowtham executing with id: 1

预期输出是具有唯一ID的线程:

Sampath executing with id: 1
Harish executing with id: 2
Harsha executing with id: 3
Gowtham executing with id: 4              

4 个答案:

答案 0 :(得分:2)

您的代码不是线程安全的,因为class CustomerThread extends Thread { private static final AtomicInteger prevCustId = new AtomicInteger(); private final int custId; CustomerThread(String name) { super(name); this.custId = prevCustId.incrementAndGet(); } @Override public void run() { System.out.println(getName() + " executing with id: " + this.custId); } } 运算符不是线程安全的。

您应该使用AtomicInteger,并且没有理由使用ThreadLocal

将您的班级更改为此,在创建时分配ID,即按创建顺序分配,而不是在首次使用时延期:

Sampath executing with id: 1
Harsha executing with id: 3
Gowtham executing with id: 4
Harish executing with id: 2

示例输出

{{1}}

答案 1 :(得分:1)

您无法在不同的主题中安全地增加Integer,您应该使用AtomicInteger getAndIncrement()方法来处理您的情况。

答案 2 :(得分:1)

您的代码存在两个问题:

  1. 非线程安全递增静态custId值
  2. 每次创建此类的新实例时都不必使用ThreadLocal变量(即,您的类的单个实例不是跨线程共享的,而是每个线程都有自己的类实例)。
  3. 问题1的修复是使用AtomicInteger,还是在同步块中执行增量操作。

    问题2的修复是简单地删除静态ThreadLocal变量,只使用常规的非静态变量。

    代码的固定版本:

    public class ThreadLocalDemo
    {
    
        public static void main(String[] args) throws InterruptedException {
            CustomerThread custThread1 = new CustomerThread("Sampath");
            CustomerThread custThread2 = new CustomerThread("Harish");
            CustomerThread custThread3 = new CustomerThread("Harsha");
            CustomerThread custThread4 = new CustomerThread("Gowtham");
            custThread1.start();
            custThread2.start();
            custThread3.start();
            custThread4.start();
        }
    }
    
    class CustomerThread extends Thread {
        static AtomicInteger custId = new AtomicInteger(0);
        private int tl;
    
        CustomerThread(String name) {
            super(name);
            tl = custId.incrementAndGet();
        }
    
        public void run() {
            System.out.println(Thread.currentThread().getName() + " executing with id: " + tl);
        }
    
    }
    

答案 3 :(得分:1)

你在这里看到的是ThreadLocal.initialValue()并且整数增量在默认情况下不是线程安全的,因此它们的组合也不会成为线程安全的。

'官方'ThreadLocal示例使用AtomicInteger(这也是其他人的建议),这使整数增量成为线程安全的。但是你也可以自由地使initialValue()方法成为线程安全的,synchronized

// ... your original code ...
    synchronized protected Integer initialValue() {
// ... your original code ...

然后您的代码可以使用简单的Integer

正常工作