ThreadLocal的接口方法:
- void set(Object value):设置当前线程的线程局部变量的值。
- public Object get():该方法返回当前线程所对应的线程局部变量。
- public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。
需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本。
下面是一个多线程向一个表插入数据的例子:
package com.lucky.concurrent;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;public class ThreadLocalDemo extends Thread { @Override public void run() { ThradTask.executeUpdate(); } public static void main(String[] args) { for (int i = 0; i < 900; i++) { ThreadLocalDemo demo = new ThreadLocalDemo(); demo.start(); } }}class ThradTask { static String sql = "insert into job_log(id, jobtype, logdate, status, ext1, ext2, ext3, log_time) values (seq_job_log.nextval, 1, sysdate, 1, null, null, null, sysdate)"; public static void executeUpdate() { Connection con = null; try { con = DbUtil.getConnection(); con.setAutoCommit(false); // 关闭自动提交事务(开启事务) con.prepareStatement(sql).executeUpdate(); con.commit(); } catch (SQLException e) { e.printStackTrace(); } finally { DbUtil.closeConnection(); System.out.println("insert success "); } }}class DbUtil { private static Connection con;// 静态连接对象,全部对象共享一个 public static Connection getConnection() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); con = DriverManager.getConnection( "jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl", "dev_tc", "123456"); } catch (Exception e) { e.printStackTrace(); } return con; } public static void closeConnection() { if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }}看结果:
数据库关闭的连接,这个错误跟 Connection 有关系,那我就将主要精力放在检查 Connection 相关的代码上吧。是不是 Connection 不应该是 static 的呢?我当初设计成 static 的主要是为了让 DBUtil 的 static 方法访问起来更加方便,用 static 变量来存放 Connection 也提高了性能啊。怎么搞呢?接着就是重构了DbUtil。看下面的代码。只贴出DbUtil的代码。
class DbUtil { private static ThreadLocal运行结果:local = new ThreadLocal (); public static Connection getConnection() { try { Connection localCon = local.get(); if (localCon == null) { Class.forName("oracle.jdbc.driver.OracleDriver"); localCon = DriverManager.getConnection( "jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl", "dev_tc", "123456"); local.set(localCon); } } catch (Exception e) { //获取链接报错,比如超过最大连接数则销毁当前任务。 <正常情况下是不能这样做> Thread.currentThread().stop(); e.printStackTrace(); } return local.get(); } public static void closeConnection() { Connection con = local.get(); if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); }finally{ local.remove();//将当前线程局部变量的值删除,目的是为了减少内存的占用 } } }} 正常情况下是不能这样做>
下面是另一个例子:
package test;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadLocalTest { private static class Task implements Runnable { private static ThreadLocalnumber = new ThreadLocal () { @Override protected Integer initialValue() { return 0; } }; public int getNumber() { number.set(number.get() + 1); return number.get(); } @Override public void run() { try { Thread.sleep(new Random().nextInt(5000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " " + getNumber()); } } public void run() { ExecutorService pool = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { pool.execute(new ThreadLocalTest.Task()); } pool.shutdown(); } public static void main(String[] args) { new ThreadLocalTest().run(); }}
运行结果:每一个线程初始化值都是1。获得的结果就是static变量没有多线程共享。多线程安全。
此文章是学习了 这位大牛的而出世的。
下次继续写。先睡觉。