008-ThreadLocal

Posted 木子旭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于008-ThreadLocal的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器