1.10 错误的使用ThreadLocal导致线程不安全

错误使用ThreadLocal导致线程不安全

public class ThreadLocalFault extends Thread{
    private static User user = new User(0);
    ThreadLocal<User> local = new ThreadLocal<>();
    @Override
    public void run() {
        user.setAge(user.getAge()+1);
        local.set(user);
        System.out.println(Thread.currentThread().getName()+":"+local.get().getAge());
    }
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadLocalFault().start();
        }
    }
    static class User{
        int age;
        public User(int age) {
            this.age = age;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
}

运行后的结果为

Thread-2:3
Thread-3:4
Thread-0:3
Thread-1:3
Thread-4:5

而预料中的正确结果应该都是1

原因分析

为什么ThreadLocal在这里会产生错误的结果,是因为我们Thread 的变量ThreadLocalMap存储的是对象的一个引用,一个线程修改对象值,其他引用对象的值也会发生改变。因为private static User user = new User(0);,使用static修饰,是静态变量五个线程的ThreadLocal保存的是同一个对象的引用,所以结果就产生了偏差

解决方法

方法一:

去掉private static User user = new User(0);中的static
public class ThreadLocalFault extends Thread{
    private User user = new User(0);
    ThreadLocal<User> local = new ThreadLocal<>();
    @Override
    public void run() {
        user.setAge(user.getAge()+1);
        local.set(user);
        System.out.println(Thread.currentThread().getName()+":"+local.get().getAge());
    }
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadLocalFault().start();
        }
    }
    static class User{
        int age;
        public User(int age) {
            this.age = age;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
}

结果 

Thread-4:1
Thread-1:1
Thread-0:1
Thread-3:1
Thread-2:1

方法二:每次new ThreadLocal的时候,在实现initial方法中,新建一个User对象

public class ThreadLocalFault extends Thread{
    //private User user = new User(0);
    ThreadLocal<User> local = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new User(0);
        }
    };
    @Override
    public void run() {
        local.get().setAge(local.get().getAge()+1);
        //local.set(user);
        System.out.println(Thread.currentThread().getName()+":"+local.get().getAge());
    }
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadLocalFault().start();
        }
    }
    static class User{
        int age;
        public User(int age) {
            this.age = age;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
}

结果

Thread-4:1
Thread-1:1
Thread-0:1
Thread-3:1
Thread-2:1

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章