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

 

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