Java多线程编程概述
Java多线程的安全问题
Java多线程同步
Java多线程间的通信
Java线程Lock
Java多线程管理
保障线程安全的设计技术
Java锁的优化及注意事项
Java多线程集合
【Java多线程】单例模式与多线程

Java ThreadLocal使用

除了控制资源的访问外, 还可以通过增加资源来保证线程安全,ThreadLocal主要解决为每个线程绑定自己的值。

package com.wkcto.threadlocal;

/**
 * ThreadLocal的基本使用
 */
public class Test01 {
    //定义ThreadLocal对象
    static  ThreadLocal threadLocal = new ThreadLocal();
    //定义线程类
    static  class  Subthread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                //设置线程关联的的值
                threadLocal.set( Thread.currentThread().getName() + " - " + i);
                //调用get()方法读取关联的值
                System.out.println(Thread.currentThread().getName() + " value = " + threadLocal.get());
            }
        }
    }

    public static void main(String[] args) {
        Subthread t1 = new Subthread();
        Subthread t2 = new Subthread();
        t1.start();
        t2.start();

    }
}
package com.wkcto.threadlocal;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *  在多线程环境中,把字符串转换为日期对象,多个线程使用同一个SimpleDateFormat对象可能会产生线程安全问题,有异常
 *  为每个线程指定自己的SimpleDateFormat对象, 使用ThreadLocal
 */
public class Test02 {
    //定义SimpleDateFormat对象,该对象可以把字符串转换为日期
//    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
    static ThreadLocal threadLocal = new ThreadLocal<>();
    //定义Runnable接口的实现类
    static class ParseDate implements Runnable{
        private  int i = 0 ;
        public ParseDate(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            try {
                String text = "2068年11月22日 08:28:" + i%60;      //构建日期字符串
//                Date date = sdf.parse(text);        //把字符串转换为日期
                //先判断当前线程是否有SimpleDateFormat对象,如果当前线程没有SimpleDateFormat对象就创建一个,如果有就直接使用
                if (threadLocal.get() == null){
                    threadLocal.set(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"));
                }
                Date date = threadLocal.get().parse(text);
                System.out.println(i + " -- " + date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //创建100个线程
        for (int i = 0; i < 100; i++) {
            new Thread(new ParseDate(i)).start();
        }
    }
}
package com.wkcto.threadlocal;

import java.util.Date;
import java.util.Random;

/**
 * ThreadLocal初始值, 定义ThreadLocal类的子类,在子类中重写initialValue()方法指定初始值,再第一次调用get()方法不会返回null
 */
public class Test03 {
    //1) 定义ThreadLocal的子类
    static class SubThreadLocal extends ThreadLocal{
        // 重写initialValue方法,设置初始值
        @Override
        protected Date initialValue() {
//            return new Date();  //把当前日期设置为初始化
            return  new Date(System.currentTimeMillis() - 1000*60*15);
        }
    }

    //定义ThreadLocal对象
//    static ThreadLocal threadLocal = new ThreadLocal();
    //直接使用自定义的SubThreadLocal对象
    static SubThreadLocal threadLocal = new SubThreadLocal();

    //定义线程类
    static class SubThread extends  Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                //第一次调用threadLocal的get()方法会返回null
                System.out.println("---------" + Thread.currentThread().getName() + " value=" + threadLocal
                .get());
                //如果没有初始值就设置当前日期
                if ( threadLocal.get() == null ){
                    System.out.println("*****************");
                    threadLocal.set(new Date());
                }
                try {
                    Thread.sleep(new Random().nextInt(500));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        SubThread t1 = new SubThread();
        t1.start();
        SubThread t2 = new SubThread();
        t2.start();
    }
}