• 008-ThreadLocal


    一、基本用法

      ThreadLocal是一个容器,用于存放线程的局部变量。如果ThreadLocalVariable(线程局部变量)更加好理解。

      在Jdk 1.2 java.lang.ThreadLocal开始使用,他是为解决多线程并发设计的.

      示例序列号生成,保证每个线程生成唯一序列号

    编写Sequence接口

    package com.lhx.chapter4.threadlocaltest;
    
    public interface Sequence {
        int getNumber();
    }
    View Code

    编写线程类

    package com.lhx.chapter4.threadlocaltest;
    
    public class ClientThread extends Thread {
        private Sequence sequence;
    
        public ClientThread(Sequence sequence){
            this.sequence=sequence;
        }
    
        @Override
        public void run() {
            for(int i=0;i<3;i++){
                System.out.println(Thread.currentThread().getName()+"=>"+sequence.getNumber());
            }
        }
    }
    View Code

    实现方式一

    package com.lhx.chapter4.threadlocaltest;
    
    public class SequenceA implements Sequence {
        private static int number = 0;
        @Override
        public int getNumber() {
            number=number+1;
            return number;
        }
    
        public static void main(String[] args) {
            Sequence sequence = new SequenceA();
            ClientThread clientThread1 = new ClientThread(sequence);
            ClientThread clientThread2 = new ClientThread(sequence);
            ClientThread clientThread3 = new ClientThread(sequence);
    
            clientThread1.start();
            clientThread2.start();
            clientThread3.start();
        }
    }
    View Code
    Thread-0=>1
    Thread-1=>2
    Thread-0=>3
    Thread-1=>4
    Thread-0=>5
    Thread-1=>6
    Thread-2=>7
    Thread-2=>8
    Thread-2=>9

    由输出可以线程间共享一个Static变量,无法保证线程安全。

    示例2,

    package com.lhx.chapter4.threadlocaltest;
    
    public class SequenceB implements Sequence {
        private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue() {
                return 0;
            }
        };
        @Override
        public int getNumber() {
            numberContainer.set(numberContainer.get()+1);
            return numberContainer.get();
        }
    
        public static void main(String[] args) {
            Sequence sequence = new SequenceB();
            ClientThread clientThread1 = new ClientThread(sequence);
            ClientThread clientThread2 = new ClientThread(sequence);
            ClientThread clientThread3 = new ClientThread(sequence);
    
            clientThread1.start();
            clientThread2.start();
            clientThread3.start();
        }
    }
    View Code

    输出值:

    Thread-0=>1
    Thread-0=>2
    Thread-0=>3
    Thread-1=>1
    Thread-1=>2
    Thread-1=>3
    Thread-2=>1
    Thread-2=>2
    Thread-2=>3

    可以看出每个线程变量独立开来,也就是说ThreadLocal为每个线程提供了一个副本。

    ThreadLocal的API,

     public void set(T value)将值放入线程局部变量中

    public T get() 从线程局部变量中获取值
    public void remove()从线程局部变量中移除
    protected T initialValue()返回线程局部变量的初始值(默认为null),必须默认实现。

     自定义实现ThreadLocal

    package com.lhx.chapter4.threadlocaltest;
    
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    public class MyThreadLocal<T> {
        private Map<Thread,T> container=Collections.synchronizedMap(new HashMap<Thread,T>());
        public void set(T value){
            container.put(Thread.currentThread(),value);
        }
        public T get(){
            Thread thread = Thread.currentThread();
            T value = container.get(thread);
            if(value==null&&!container.containsKey(thread)){
                value=initialValue();
                container.put(thread,value);
            }
            return value;
        }
        public void remove(){
            container.remove(Thread.currentThread());
        }
    
        protected T initialValue(){
            return null;
        }
    
    }
    View Code

    用法一致

    二、使用案例

        通过ThreadLocal存放Jdbc Connection,以达到事务控制能力。

      示例,修改价格,记录日志

      update product set price=? where id=?

      insert into log(crerated,description)values(?,?)

    编写工具类DBUtil  

    package com.lhx.test.dbutil1;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class DBUtil {
        private static final String driver = "com.mysql.jdbc.Driver";
        private static final String url = "jdbc:mysql://localhost:3306/demo";
        private static final String username = "root";
        private static final String password = "root";
    
        private static Connection conn = null;
    
        public static Connection getConnection() {
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, username, password);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return conn;
        }
    
        public static void colseConnection() {
            try {
                if(conn!=null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    View Code

    编写ProductService

    package com.lhx.test.dbutil1;
    
    public interface ProductService {
        void updateProductPrice(long productId,int price);
    }
    View Code

    编写ProductService实现类编写ProductServiceImpl

    package com.lhx.test.dbutil1;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class ProductServiceImpl implements ProductService {
    
        private static final String Update_Product_sql = "update product set price=? where id=?";
        private static final String Insert_Log_sql = "insert into log(crerated,description)values(?,?)";
    
        @Override
        public void updateProductPrice(long productId, int price) {
            try {
                Connection connection = DBUtil.getConnection();
                connection.setAutoCommit(false);
                updateProduct(connection,Update_Product_sql,productId,price);
                insertLog(connection,Update_Product_sql,"Create Product");
                connection.commit();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                DBUtil.colseConnection();
            }
        }
        private void updateProduct(Connection connection,String sql,long productId,int price) throws Exception{
            PreparedStatement pstmt = connection.prepareStatement(sql);
            pstmt.setInt(1,price);
            pstmt.setLong(2,productId);
            int rows = pstmt.executeUpdate();
            if(rows!=0){
                System.out.println("update product success");
            }
        }
        private void insertLog(Connection connection,String sql,String msg) throws Exception{
            PreparedStatement pstmt = connection.prepareStatement(sql);
            pstmt.setString(1,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(new Date()));
            pstmt.setString(2,msg);
            int rows = pstmt.executeUpdate();
            if(rows!=0){
                System.out.println("insert log success");
            }
        }
    }
    View Code

    个人测试

    public static void main(String[] args) {
         ProductService productService = new ProductServiceImpl();
         productService.updateProductPrice(1,3000);
    }
    View Code

    并发测试

    package com.lhx.test.dbutil1;
    
    import com.lhx.test.dbutil1.ProductService;
    
    public class ClientThread extends Thread {
        private ProductService productService;
    
    
        public ClientThread(ProductService productService) {
            this.productService = productService;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            productService.updateProductPrice(1, 3000);
        }
    }
    View Code
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                ProductService productService = new ProductServiceImpl();
                ClientThread clientThread = new ClientThread(productService);
                clientThread.start();
            }
        }
    View Code

    使用ThreadLocal编写工具类

    package com.lhx.test.dbutil2;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    
    public class DBUtil {
        private static final String driver = "com.mysql.jdbc.Driver";
        private static final String url = "jdbc:mysql://localhost:3306/demo";
        private static final String username = "root";
        private static final String password = "root";
    
        private static ThreadLocal<Connection> conContainer = new ThreadLocal<>();
    
        public static Connection getConnection() {
            Connection conn = conContainer.get();
            try {
                if (conn == null) {
                    Class.forName(driver);
                    conn = DriverManager.getConnection(url, username, password);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            finally {
                conContainer.set(conn);
            }
            return conn;
        }
    
        public static void colseConnection() {
            Connection conn = conContainer.get();
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            finally {
                conContainer.remove();
            }
        }
    }
    View Code
  • 相关阅读:
    shell编程介绍
    第一章作业
    jenkins介绍与操作
    gitlab介绍与操作
    github介绍与操作
    git介绍与操作
    zabbix监控tomcat与安全规范
    js-20170605-基本语法
    webstorm激活方法
    mac的一些基本设置(分享篇)
  • 原文地址:https://www.cnblogs.com/bjlhx/p/7841463.html
Copyright © 2020-2023  润新知