Java concurrency 06---ThreadLocal class and application skills



  The previous section summarized the data sharing problem within the thread scope, that is, define a Map, store the current thread name and the data in the thread in the Map in the form of key-value pairs, and then use the data in the current thread. The data in the current thread can be obtained from the Map according to the name of the current thread, so that the data between different threads does not interfere with each other. In fact, the ThreadLocal class provides us with this solution, so we can use ThreadLocal to complete the sharing of data within the thread scope.

public class ThreadScopeShareData {
    //定义一个ThreadLocal
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();

    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 a data: " + data);
                    threadLocal.set(data);//直接往threadLocal里面里面扔数据即可
                    new TestA().getData();
                    new TestB().getData();
                }
            }).start();
        }
    }

    static class TestA {
        public void getData() {
            System.out.println("A get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么关键字,它直接从当前线程中取
        }
    }

    static class TestB {
        public void getData() {
            System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么关键字,它直接从当前线程中取
        }
    }
}
  
  

      Combined with the code in the previous section, it can be seen that ThreadLocal is actually equivalent to a Map, but we do not need to set the key, it is the current Thread by default, put data in it, set it directly, get the data, directly Just get it, it’s very convenient, you don’t need to save one by one in Map, but here comes the problem. Although ThreadLocal is easy to access, there are no parameters in the get() method at all, that is to say, we can only put one data in ThreadLocal. It won't work, so how to solve this problem?
      Obviously, ThreadLocal is a container and can only store one, so if there are multiple data, we can define a class, encapsulate the data into this class, and then throw it into ThreadLocal, take this class when we use it, and then from Just go to the data we want in the class.
    Well, now there are two threads, each thread has to operate its own data, and there are two data: name and age. According to the above ideas, write a demo, as follows:

    public class ThreadScopeShareData {
    
        private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();
    
        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 a data: " + data);
    
                        //每个线程中维护一个User,User中保存了name和age
                        User user = new User();
                        user.setName("name" + data);
                        user.setAge(data);
                        threadLocal.set(user); //向当前线程中存入user对象
    
                        new TestA().getData();
                        new TestB().getData();
                    }
                }).start();
            }
        }
    
        static class TestA {
            public void getData() {
    
                User user = threadLocal.get();//从当前线程中取出user对象
                System.out.println("A get data from " + Thread.currentThread().getName() + ": " 
                        + user.getName() + "," + user.getAge());
            }
        }
    
        static class TestB {
            public void getData() {
    
                User user = threadLocal.get();//从当前线程中取出user对象
                System.out.println("B get data from " + Thread.currentThread().getName() + ": " 
                        + user.getName() + "," + user.getAge());
    
            }
        }
    
    }
    //定义一个User类来存储姓名和年龄
    class User {
    
        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;
        }   
    }
      
      

        Encapsulation in this way can realize the storage of multiple data, but the above program is not very good, the reason is obvious, in the thread, I have to create a new object by myself, and then operate on it, and finally have to put this The object is thrown into the current thread. This is not in line with the design idea. The design idea should be like this. You can't let the user go to new. If there is a similar getThreadInstance()method, the user can use the object to call this getThreadInstance()method. Well, what you get in this way is always the object within the scope of this thread.
        This reminds me of the practice of taking the connection from ThreadLocal when I was learning JDBC. If there is one in the current ThreadLocal, I will take it out. If there is no one, I will generate one. This is the same as the requirement here. I should use User to call getThreadLInstance()the method to get a User object in this thread, if there is one, take it, if not, generate one. Exactly the same idea. This design is a bit similar to the singleton pattern. Here, the similarity is not in essence, but in the code structure. Let's take a look at the simple singleton pattern code structure:

      public class Singleton {
          private static Singleton instance = null;
          private Singleton() {//私有构造方法阻止外界new        
          }
          public static synchronized Singleton getInstance() {  //提供一个公共方法返回给外界一个单例的实例
              if (instance == null) {  //如果没有实例
                  instance = new Singleton();  //就新new一个
              }  
              return instance;  //返回该实例
          } 
      }
        
        

          This is the code structure of the lazy singleton mode. We can follow this idea to design a way to get the User from the current thread, so the program is modified as follows:

        public class ThreadScopeShareData {
        //不需要在外面定义threadLocal了,放到User类中了
        //  private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();
        
            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 a data: " + data);
        
                            //这里直接用User去调用getThreadLocal这个静态方法获取本线程范围内的一个User对象
                            //这里就优雅多了,我完全不用关心如何去拿该线程中的对象,如何把对象放到threadLocal中
                            //我只要拿就行,而且拿出来的肯定就是当前线程中的对象,原因看下面User类中的设计
                            User.getThreadInstance().setName("name" + data);
                            User.getThreadInstance().setAge(data);
        
                            new TestA().getData();
                            new TestB().getData();
                        }
                    }).start();
                }
            }
        
            static class TestA {
                public void getData() {
                    //还是调用这个静态方法拿,因为刚刚已经拿过一次了,threadLocal中已经有了
                    User user = User.getThreadInstance();
                    System.out.println("A get data from " + Thread.currentThread().getName() + ": " 
                            + user.getName() + "," + user.getAge());
                }
            }
        
            static class TestB {
                public void getData() {
        
                    User user = User.getThreadInstance();
                    System.out.println("A get data from " + Thread.currentThread().getName() + ": " 
                            + user.getName() + "," + user.getAge());
                }
            }
        
        }
        
        class User {
        
            private User() {}
        
            private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();
        
            //注意,这不是单例,每个线程都可以new,所以不用synchronized,
            //但是每个threadLocal中是单例的,因为有了的话就不会再new了
            public static /*synchronized*/ User getThreadInstance() {
                User instance = threadLocal.get(); //先从当前threadLocal中拿
                if(instance == null) {
                    instance = new User();
                    threadLocal.set(instance);//如果没有就新new一个放到threadLocal中
                }
                return instance; //向外返回该User
            }
        
            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;
            }
        }
          
          

            After such a transformation, the code is much more elegant. The outside world never thinks about how to get data from the current thread. Just get it, and it must be the object you want in the current thread, because this has been written inside the object. It is a static method, and after it is taken out and operated, there is no need to put it in threadLocal, because it is already in threadLocal, which is quite well encapsulated.
            The application and usage skills of the ThreadLocal class are summarized so much~
            
            




            The previous section summarized the data sharing problem within the thread scope, that is, define a Map, store the current thread name and the data in the thread in the Map in the form of key-value pairs, and then use the data in the current thread. The data in the current thread can be obtained from the Map according to the name of the current thread, so that the data between different threads does not interfere with each other. In fact, the ThreadLocal class provides us with this solution, so we can use ThreadLocal to complete the sharing of data within the thread scope.

          Guess you like

          Origin http://10.200.1.11:23101/article/api/json?id=326877603&siteId=291194637