1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > ThreadLocal一次性解决老大难问题

ThreadLocal一次性解决老大难问题

时间:2018-11-01 23:00:44

相关推荐

ThreadLocal一次性解决老大难问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

ThreadLocal

Threadlocal使用场景一、故事从两个线程开始二、线程池来帮忙,却好心帮坏事1. 10个线程打印日期2、1000个线程打印日期3.使用Synchronized解决线程安全问题4.使用ThreadLocal解决线程安全问题三、 ThreadLocal第二种用法四.总结两个作用两个场景使用ThreadLocal的好处

Threadlocal使用场景

一、故事从两个线程开始

ThreadLocal与synchronized同步机制的比较

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

ThreadLocal是线程局部变量,是一种多线程间并发访问变量的解决方案。和synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的方式,为每个线程提供变量的独立副本,以保证线程的安全。

代码如下(示例):

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;/*** 描述:两个线程打印日期*/public class ThreadLocalNormalUsage00 {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage00().date(10);System.out.println(date);}}).start();new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage00().date(1047);System.out.println(date);}}).start();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}}

二、线程池来帮忙,却好心帮坏事

1. 10个线程打印日期

代码如下(示例):

10个线程打印日期

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;/*** 描述:10个线程打印日期*/public class ThreadLocalNormalUsage01 {public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 30; i++) {int finalI = i;new Thread(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage01().date(finalI);System.out.println(date);}}).start();Thread.sleep(100);}}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}}

结果:

2、1000个线程打印日期

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.Executor;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 描述:1000个打印日期的任务,用线程池来执行*/public class ThreadLocalNormalUsage02 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage02().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return dateFormat.format(date);}}

结果:

将ThreadLocalNormalUsage02改进后,1000个线程共享DateFormat对象

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 描述:1000个打印日期的任务,用线程池来执行*/public class ThreadLocalNormalUsage03 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage03().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);return dateFormat.format(date);}}

结果:

发现发生了线程安全问题,所有线程共用一个SimpleDateFormat,有些时间是一样的

3.使用Synchronized解决线程安全问题

使用Synchronized解决线程安全问题

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 描述:加锁来解决线程安全问题*/public class ThreadLocalNormalUsage04 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage04().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);String s = null;synchronized (ThreadLocalNormalUsage04.class) {s = dateFormat.format(date);}return s;}}

结果:

虽然解决了并发线程安全问题,但是效率太低了。


4.使用ThreadLocal解决线程安全问题

提示:这里对文章进行总结:

由于有10个线程执行任务(打印日期),创建10个dateformat对象,这样既没有性能损耗,保证线程安全(每个dateformat独享独有),也避免了synchornized带来的一直等待排队的问题。

利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存

package threadlocal;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 描述:利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存*/public class ThreadLocalNormalUsage05 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage05().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get();return dateFormat.format(date);}}class ThreadSafeFormatter {public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));}

运行结果:

三、 ThreadLocal第二种用法

这里每个线程内保存全局变量,可以让不同方法直接使用,避免参数传递麻烦

## 更好的方法是用Threadlocal,这样无需synchronized,可以在不影响性能的情况下,无需层层传递参数,就可达到保存当前线程对应的用户信息的目的。

package threadlocal;/*** 描述:演示ThreadLocal用法2:避免传递参数的麻烦*/public class ThreadLocalNormalUsage06 {public static void main(String[] args) {new Service1().process("");}}class Service1 {public void process(String name) {User user = new User("超哥");UserContextHolder.holder.set(user);new Service2().process();}}class Service2 {public void process() {User user = UserContextHolder.holder.get();ThreadSafeFormatter.dateFormatThreadLocal.get();System.out.println("Service2拿到用户名:" + user.name);new Service3().process();}}class Service3 {public void process() {User user = UserContextHolder.holder.get();System.out.println("Service3拿到用户名:" + user.name);UserContextHolder.holder.remove();}}class UserContextHolder {public static ThreadLocal<User> holder = new ThreadLocal<>();}class User {String name;public User(String name) {this.name = name;}}

运行结果:

四.总结

两个作用

两个场景

使用ThreadLocal的好处

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。