ThreadLocal 简介

      ThreadLocal 让每个线程可以保留一份变量的私有"版本" 。 早在 JDK 1.2 的版本中就提供 java.lang.ThreadLocal ThreadLocal 为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。 ThreadLocal 并不是一个 Thread ,而是 Thread 的局部变量。当使用 ThreadLocal 维护变量时, ThreadLocal 为每个使用该变量的线程提供独立的变量副本。

ThreadLocal的接口方法:

  1. void set(Object value):设置当前线程的线程局部变量的值。
  2. public Object get():该方法返回当前线程所对应的线程局部变量。
  3. 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();
			}
		}
	}
}
看结果:
ThreadLocal 简介_第1张图片


数据库关闭的连接,这个错误跟 Connection 有关系,那我就将主要精力放在检查 Connection 相关的代码上吧。是不是 Connection 不应该是 static 的呢?我当初设计成 static 的主要是为了让 DBUtil 的 static 方法访问起来更加方便,用 static 变量来存放 Connection 也提高了性能啊。怎么搞呢?接着就是重构了DbUtil。看下面的代码。只贴出DbUtil的代码。
   

class DbUtil {
	private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();

	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();//将当前线程局部变量的值删除,目的是为了减少内存的占用
			}
		}
	}
}

运行结果:
ThreadLocal 简介_第2张图片

下面是另一个例子:

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 ThreadLocal<Integer> number = new ThreadLocal<Integer>() {
			@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变量没有多线程共享。多线程安全。
ThreadLocal 简介_第3张图片

此文章是学习了http://my.oschina.net/huangyong/blog/159489 这位大牛的而出世的。

下次继续写。先睡觉。




你可能感兴趣的:(thread,threadLocal)