zoukankan      html  css  js  c++  java
  • ThreadLocal

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

    另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

    如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。

    线程局部变量

    在多线程环境下,之所以会有并发问题,就是因为不同的线程会同时访问同一个共享变量,例如下面的形式

    public class MultiThreadDemo {
    
        public static class Number {
            private int value = 0;
    
            public void increase() throws InterruptedException {
                value = 10;
                Thread.sleep(10);
                System.out.println("increase value: " + value);
            }
    
            public void decrease() throws InterruptedException {
                value = -10;
                Thread.sleep(10);
                System.out.println("decrease value: " + value);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Number number = new Number();
            Thread increaseThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        number.increase();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread decreaseThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        number.decrease();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            increaseThread.start();
            decreaseThread.start();
        }
    }

    在上面的代码中,increase 线程和 decrease 线程会操作同一个 number 中 value,那么输出的结果是不可预测的,因为当前线程修改变量之后但是还没输出的时候,变量有可能被另外一个线程修改,下面是一种可能的情况:

    increase value: 10
    decrease value: 10

    这种情况显然是我们不想要的

    而ThreadLocal 就解决了这个问题

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Random;
    
    public class ThreadLocalTest {
    
        private static ThreadLocal<Integer> ThreadLocalInteger = new ThreadLocal<Integer>();
        private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>();
    
        public static void main(String[] args) {
            for (int i = 0; i < 2; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int data = new Random().nextInt();
                        System.out.println(Thread.currentThread().getName()
                                + " has put data :" + data);
                        ThreadLocalInteger.set(data);
    /*                    MyThreadScopeData myData = new MyThreadScopeData();
                        myData.setName("name" + data);
                        myData.setAge(data);
                        myThreadScopeData.set(myData);*/
                        MyThreadScopeData.getThreadInstance().setName("name" + data);
                        MyThreadScopeData.getThreadInstance().setAge(data);
                        new A().get();
                        new B().get();
                    }
                }).start();
            }
        }
    
        static class A {
            public void get() {
                int data = ThreadLocalInteger.get();
                System.out.println("A from " + Thread.currentThread().getName()
                        + " get data :" + data);
    /*            MyThreadScopeData myData = myThreadScopeData.get();;
                System.out.println("A from " + Thread.currentThread().getName() 
                        + " getMyData: " + myData.getName() + "," +
                        myData.getAge());*/
                MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
                System.out.println("A from " + Thread.currentThread().getName()
                        + " getMyData: " + myData.getName() + "," +
                        myData.getAge());
            }
        }
    
        static class B {
            public void get() {
                int data = ThreadLocalInteger.get();
                System.out.println("B from " + Thread.currentThread().getName()
                        + " get data :" + data);
                MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
                System.out.println("B from " + Thread.currentThread().getName()
                        + " getMyData: " + myData.getName() + "," +
                        myData.getAge());
            }
        }
    }
    
    class MyThreadScopeData {
        private MyThreadScopeData() {
        }
    
        public static /*synchronized*/ MyThreadScopeData getThreadInstance() {
            MyThreadScopeData instance = map.get();
            if (instance == null) {
                instance = new MyThreadScopeData();
                map.set(instance);
            }
            return instance;
        }
    
        //private static MyThreadScopeData instance = null;//new MyThreadScopeData();
        private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }

    参考文章

    https://www.cnblogs.com/zhangjk1993/archive/2017/03/29/6641745.html

  • 相关阅读:
    Completely disable mousewheel on a WinForm
    C# Form内存回收
    解锁用户
    CentOS7+JDK8编译Hadoop2.6.4
    网址收藏
    pvresize
    Linux crontab 命令格式与详细例子
    在CentOS 7中安装与配置JDK8
    Disable SELinux CentOS 7
    Python3标准库:math数学函数
  • 原文地址:https://www.cnblogs.com/tinya/p/8526783.html
Copyright © 2011-2022 走看看