夜光序言:

学会去看透一切的谜题.
Learning to see the puzzle in everything.
它们无处不在.
They're everywhere.
一旦你开始寻找,就不可能停下了
Once you start looking, it's impossible to stop.
凑巧的是 人们 以及可以体现他们所作所为的谎言和错觉,是最引人入胜的谜题。
It just so happens people,and delusions that inform everything they do,tend to be the most fascinating puzz

《福尔摩斯:基本演绎法》

正文:
 
                                              以道御术 / 以术识道



1. ThreadLocal 的使用方式

(1) 在关联数据类中创建 private static ThreadLocal

在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。
(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)

另一个例子,也是私有静态 ThreadLocal 实例:

package com.hy.多线程高并发;public class ThreadContext {private String userId;private Long transactionId;private static ThreadLocal threadLocal = new ThreadLocal() {@Overrideprotected ThreadContext initialValue() {return new ThreadContext();}};public static ThreadContext get() {return (ThreadContext) threadLocal.get(); //强转一下}public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public Long getTransactionId() {return transactionId;}public void setTransactionId(Long transactionId) {this.transactionId = transactionId;}
}
补充:在 JDK 的 API 对 ThreadLocal 私有化的说明。并举例‘线程唯一标识符’
UniqueThreadIdGenerator ,大家学习是可以结合官方 API 来学习。

2. 在 Util 类中创建 ThreadLocal

这是上面用法的扩展,即把 ThreadLocal 的创建放到工具类中。

package com.hy.多线程高并发;public class HibernateUtil {private static Log log = LogFactory.getLog(HibernateUtil.class);private static final SessionFactory sessionFactory; //定义 SessionFactorystatic {try {
// 通过默认配置文件 hibernate.cfg.xml 创建 SessionFactorysessionFactory = new Configuration().configure().buildSessionFactory();} catch (Throwable ex) {log.error("初始化 SessionFactory 失败!", ex);throw new ExceptionInInitializerError(ex);}}//创建线程局部变量 session,用来保存 Hibernate 的 Sessionpublic static final ThreadLocal session = new ThreadLocal();/*** 获取当前线程中的 Session** @return Session* @throws HibernateException*/public static Session currentSession() throws HibernateException {Session s = (Session) session.get();
// 如果 Session 还没有打开,则新开一个 Sessionif (s == null) {s = sessionFactory.openSession();session.set(s); //将新开的 Session 保存到线程局部变量中}return s;}public static void closeSession() throws HibernateException {
//获取线程局部变量,并强制转换为 Session 类型Session s = (Session) session.get();session.set(null);if (s != null)s.close();}
}

3. 在 Runnable 中创建 ThreadLocal

在线程类内部创建 ThreadLocal,基本步骤如下:

①、在多线程的类(如 ThreadDemo 类)中,创建一个 ThreadLocal 对象 threadXxx,用来保存线程间需要隔离处理的对象 xxx。
②、在 ThreadDemo 类中,创建一个获取要隔离访问的数据的方法 getXxx(),在方法中判断,若ThreadLocal 对象为 null 时候,应该 new()一个隔离访问类型的对象,并强制转换为要应用的类型
③、在 ThreadDemo 类的 run()方法中,通过调用 getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。
package com.hy.多线程高并发;import java.util.Random;public class ThreadLocalTest implements Runnable{ThreadLocal<Studen> studenThreadLocal = new ThreadLocal<Studen>();@Overridepublic void run() {String currentThreadName = Thread.currentThread().getName();System.out.println(currentThreadName + " is running...");Random random = new Random();int age = random.nextInt(100);System.out.println(currentThreadName + " is set age: " + age);Studen studen = getStudent();//通过这个方法,为每个线程都独立的 new 一个 student 对象,每个线程的的//student 对象都可以设置不同的值studen.setAge(age);System.out.println(currentThreadName + " is first get age: " + studen.getAge());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println( currentThreadName + " is second get age: " + studen.getAge());}private Studen getStudent() {Studen studen = studenThreadLocal.get();if (null == studen) {studen = new Studen();studenThreadLocal.set(studen);}return studen;}public static void main(String[] args) {ThreadLocalTest t = new ThreadLocalTest();Thread t1 = new Thread(t,"Thread A");Thread t2 = new Thread(t,"Thread B");t1.start();t2.start();}
}class Studen {int age;public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

( 5 ) 多线程共享数据

在 Java 传统线程机制中的共享数据方式,大致可以简单分两种情况:

多个线程行为一致,共同操作一个数据源
也就是每个线程执行的代码相同,可以使用同一个 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如,卖票系统就可以这么做。
多个线程行为不一致,共同操作一个数据源。也就是每个线程执行的代码不同,这时候需要用不同的Runnable 对象。
例如,银行存取款。
下面我们通过两个示例代码来分别说明这两种方式。

1. 多个线程行为一致共同操作一个数据

如果每个线程执行的代码相同,可以使用同一个 Runnable 对象,这个 Runnable 对象中有那个共享数据,例如,买票系统就可以这么做
package com.hy.多线程高并发;/**
* @Description:   多个线程行为一致共同操作一个数据* 如果每个线程执行的代码相同,可以使用同一个 Runnable 对象* 这个 Runnable 对象中有那个共享数据,例如* 买票系统就可以这么做
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/
public class Test5 {/***共享数据类**/static class ShareData{private int num = 10 ;public synchronized void inc() {num++;System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}/***多线程类**/static class RunnableCusToInc implements Runnable{private ShareData shareData;public RunnableCusToInc(ShareData data) {this.shareData = data;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.inc();}}}/***测试方法**/public static void main(String[] args) {ShareData shareData = new ShareData();for (int i = 0; i < 4; i++) {new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();}}
}

2. 多个线程行为不一致共同操作一个数据

如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对象之间的数据共享:

1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

package com.hy.多线程高并发;/**
* @Description:* 2. 多个线程行为不一致共同操作一个数据* 如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对* 象之间的数据共享:* 1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享* 数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/
public class Test6 {public static void main(String[] args) {ShareData shareData = new ShareData();for (int i = 0; i < 4; i++) {if(i%2 == 0){new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();}else{new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();}}}//封装共享数据类static class RunnableCusToInc implements Runnable{//封装共享数据private ShareData shareData;public RunnableCusToInc(ShareData data) {this.shareData = data;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.inc();}}}//封装共享数据类static class RunnableCusToDec implements Runnable{//封装共享数据private ShareData shareData;public RunnableCusToDec(ShareData data) {this.shareData = data;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.dec();}}}/***共享数据类**/static class ShareData{private int num = 10 ;public synchronized void inc() {num++;System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public void dec() {System.out.println("desc方法......");}}
}

package com.hy.多线程高并发;/**
* @Description:* 2. 多个线程行为不一致共同操作一个数据* 如果每个线程执行的代码不同,这时候需要用不同的 Runnable 对象,有如下两种方式来实现这些 Runnable 对* 象之间的数据共享:* 1) 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个 Runnable 对象。每个线程对共享* 数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/
public class Test6 {public static void main(String[] args) {ShareData shareData = new ShareData();for (int i = 0; i < 4; i++) {if(i%2 == 0){new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();}else{new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();}}}//封装共享数据类static class RunnableCusToInc implements Runnable{//封装共享数据private ShareData shareData;public RunnableCusToInc(ShareData data) {this.shareData = data;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.inc();}}}//封装共享数据类static class RunnableCusToDec implements Runnable{//封装共享数据private ShareData shareData;public RunnableCusToDec(ShareData data) {this.shareData = data;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.dec();}}}/***共享数据类**/static class ShareData{private int num = 10 ;public synchronized void inc() {num++;System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public void dec() {System.out.println("desc方法......");num--;System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

2) 将这些 Runnable 对象作为某一个类中的内部类

共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类

以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable 对象调用外部类的这些方法
package com.hy.多线程高并发;/**
* @Description:* 2) 将这些 Runnable 对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对* 共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个* Runnable 对象调用外部类的这些方法
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/public class Test7 {public static void main(String[] args) {
//公共数据final ShareData shareData = new ShareData();for (int i = 0; i < 4; i++) {if(i%2 == 0){new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.inc();} }},"Thread "+ i).start();}else{new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {shareData.dec();} }},"Thread "+ i).start();}}}//static class ShareData{private int num = 10 ;//递增public synchronized void inc() {num++;System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}//递减public synchronized void dec() {num--;System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
http://tutorials.jenkov.com/java-concurrency/index.html

夜光:Java成神之路(九)擅长的语言相关推荐

  1. Java成神之路技术整理

    转载自 Java成神之路技术整理 以下是Java技术栈微信公众号发布的所有关于 Java 的技术干货,会从以下几个方面汇总,本文会长期更新. Java 基础篇 Java 集合篇 Java 多线程篇 J ...

  2. Java成神之路[转]

    阿里大牛珍藏架构资料,点击链接免费获取 针对本文,博主最近在写<成神之路系列文章> ,分章分节介绍所有知识点.欢迎关注. 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 ...

  3. Alibaba技术专家倾心五年打造 Java成神之路:基础篇

    近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是Java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...

  4. 夜光:Java成神之路(四)擅长的语言

    夜光序言: 女孩问:"你喜欢我吗?" "不喜欢"男孩不假思索的回答 "哦"女孩失落地低下头,眼泪快要掉下来 "傻瓜,我不喜欢你,我 ...

  5. 夜光:Java成神之路(二)擅长的语言

    夜光序言: 命中注定的人,并不是注定与之相爱.结合.交配的人. 命中注定的人,是为了令你成为你注定成为的样子而出现的人. 正文:                                     ...

  6. 夜光带你走进 Java 成神之路--Spring(四十六)擅长的领域

    夜光序言: 人生不要被过去所控制,决定你前行的,是未来; 人生不要被安逸所控制,决定你成功的,是奋斗; 人生不要被别人所控制,决定你命运的,是自己; 人生不要被金钱所控制,决定你幸福的,是知足; 人生 ...

  7. Java成神之路——ASM,Javassist,cglib区别。

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  8. Java成神之路技术整理,本文长期更新!

    原文地址:https://mp.weixin.qq.com/s/N507Cfb_mbkGvHtg_FIaVg(来源:java技术栈微信公众号) 以下是Java技术栈微信公众号发布的所有关于 Java ...

  9. 【学海无涯】Java成神之路

    基础篇 面向对象 面向对象与面向过程   面向过程就是按照程序进行的顺序依次编写索要完成相应任务的方法,依次调用.面型对象注重对逻辑概念的封装,将若干变量和方法封装成类,各个对象互相调用.面向对象占用 ...

最新文章

  1. Python工程目录组织
  2. python下载安装教程3.8.0-Python3.8下载
  3. Aizu 0525 Osenbei(状压+贪心)
  4. (OPC Client .NET 开发类库)网上很多网友都有提过,.NET开发OPC Client不外乎下面三种方法...
  5. head.s 分析——Linux-0.11 学习笔记(三)
  6. boost::outcome模块constexpr相关的测试程序
  7. The SetStack Computer
  8. mailto发送邮件
  9. python做插件应用_Python插件机制实现详解
  10. c++ 单例模式_你真的了解单例模式吗
  11. GAE-BBS v.10 开源下载
  12. word转pdf实现,POIXMLDocumentPart.getPackageRelationship()Lorg...问题,以及NotOfficeXmlFileException...问题
  13. 设计一个python程序来计算显示通过如图2-7所示的管道_python程序设计习题与答案...
  14. C++ BMP转JPG方法二
  15. kali中rarcrack命令爆破rar压缩包密码
  16. linux服务器下如何显示中文的图片,Linux服务器中文显示问题
  17. Premiere导出视频音画不同步的解决方案
  18. 华为p40pro升级鸿蒙系统好吗现在,2021年不得不说的,华为“鸿蒙系统”,你的手机升级了吗?...
  19. 在Power BI中用DAX新建列的方式进行累计求和
  20. Matlab修改Consolas字体

热门文章

  1. SVN提交成功,但是版本库里面的内容没有更新--解决方案
  2. edge浏览器如何把网页放到桌面_edge浏览器怎么把网页放桌面 - 卡饭网
  3. 线性稳压器与开关稳压器的对比分析
  4. DBU时崩溃提示occcnt.cpp第925出错的问题解决办法
  5. java 多线程 售票_Java 多线程 之 火车站售票实例
  6. java lcs_LCS最长公共子序列java实现
  7. 寻迹Arduino智能小车
  8. 简单算法之丢手绢游戏/c++
  9. 用C语言进行Windows编程入门
  10. JavaScript / Summary of Interview Questions