打赏

Java 并发系列之四:java 多线程

1. 线程简介

2. 启动和终止线程

3. 对象及变量的并发访问

4. 线程间通信

5. 线程池技术

6. Timer定时器

7. 单例模式

8. SimpleDateFormat

9. txt

  1 java并发基础线程
  2     线程简介
  3         什么是多线程?
  4             操作系统调度的最小单元,可以使用 JMX (ThreadMXBean)来查看一个普通的Java程序包含哪些线程
  5             可以使用 jstack  查看运行时的线程信息
  6         为什么要使用多线程?
  7             1. 更多的处理器核心
  8             2. 更快的响应时间
  9                 特别是多业务操作更加快速
 10             3. 更好的编程模型
 11                 Java提供了良好、考究并且一致的多线程编程模型
 12         线程优先级
 13             背景
 14                 CPU分配时间片给线程,时间片用完就会发生线程调度,分配到时间片的多少决定了线程使用处理器资源的多少
 15             作用
 16                 线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。
 17             Java
 18                 优先级的范围是1~10,默认优先级是5,优先级搞得线程分配时间片的数量要多于优先级低的线程
 19                 设置线程优先级时,针对频繁阻塞(休眠/IO操作)的线程需要设置设计高优先级,而偏重计算(需要较多CPU时间/偏运算)的线程需要设置较低的优先级,确保处理器不会被独占。
 20                 ps:线程优先级不能作为程序正确性的依赖,因为有些操作系统会忽略Java线程对优先级的设定。
 21         线程的状态
 22             6大状态
 23                 NEW
 24                     初始状态,线程被构建,但是还没有调用start()方法
 25                 RUNNABLE
 26                     运行状态,Java线程将操作系统中的就绪和运行两种状态统称为“运行状态”
 27                 BLOCK
 28                     阻塞状态,表示线程阻塞于锁
 29                 WAITING
 30                     等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或者等待)
 31                 TIME_WAITING
 32                     超时等待状态,可以在等待的时间自行返回的
 33                 TERMINATED
 34                     终止状态,表示当前线程已经执行完毕
 35             Java状态转移
 36                 WAITING-->RUNNABLE
 37                     Object.notify()
 38                     Object.notifyAll()
 39                     LockSupport.unpark(Thread)
 40                 TIME_WAITING-->RUNNABLE
 41                     Object.notify()
 42                     Object.notifyAll()
 43                     LockSupport.unpark(Thread)
 44                 BLOCK-->RUNNABLE
 45                     获取到锁
 46                 1. 实例化后还未start()方法时的状态 New
 47                 2. New-->RUNNABLE
 48                     系统调度
 49                     Thread.start()
 50                         running-->ready
 51                             Thread.yield
 52                         ready-->running
 53                 3. RUNNABLE-->WAITING
 54                     Object.wait()
 55                     Thread.join()
 56                     LockSupport.park()
 57                 4. RUNNABLE-->TIME_WAITING
 58                     Object.wait(long)
 59                     Thread.sleep(long)
 60                     Thread.join(long)
 61                     LockSupport.parkNanos()
 62                     LockSupport.parkUntil()
 63                 5. RUNNABLE-->BLOCKED
 64                     等待进入synchronized方法
 65                     等待进入synchronized块
 66                 6. RUNNABLE-->TERMINATED
 67                     run方法结束
 68                 Java 线程状态变迁
 69                 yield
 70                     暂停当前正在执行的线程对象。
 71                     yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
 72                     yield()只能使同优先级或更高优先级的线程有执行的机会。 
 73                     注意:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
 74             注意
 75                 等待进入synchronized方法/块 为阻塞状态
 76                 java.concurrent.Lock为   等待状态,因为Lock接口对于阻塞的实现使用了LockSupport类中的相关方法
 77         Daemon线程
 78             守护线程
 79                 特殊的线程,陪伴的含义,当进程中不存在非守护线程了,则守护线程自动销毁。
 80                 典型的守护线程是垃圾回收线程
 81                 作用是为其他线程提供便利服务
 82             是一种支持性线程,主要是用在后台程序做一些后台调度与支持性工作。这意味着当JVM中没有非Daemon线程时,JVM将自动退出。
 83             可以通过调用Thread.setDaemon(true)方法将线程设为Daemon线程。(注:该方法必须在start()或者run()方法前执行,也就是说必须在线程启动前执行)
 84             注:Daemon线程被用作完成支持性工作,但是在java虚拟机退出时,Daemon线程中的finally块并不一定会执行。在构建Daemon时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
 85         程序 VS 进程 VS 线程
 86             程序
 87                 一组指令的有序结合,是静态的指令,是永久存在的
 88             进程
 89                 具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源(打开的文件,创建的socket)分配和调度的一个独立单元。进程的存在是暂时的,是一个动态的概念。
 90             线程
 91                 线程是CPU调度和分配的基本单位,是比进程更小的能独立运行的基本单元。本身基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈)。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
 92             进程 VS 线程
 93                 定义
 94                     进程是资源分配和调度的基本单位;线程是CPU/任务调度和执行的基本单位
 95                 包含关系
 96                     1个进程包含有多(大于等于1)个线程; 线程是进程的一部分(轻量级进程)
 97                 地址空间
 98                     进程之间地址空间独立; 同一进程内的线程共享本进程的地址空间
 99                 切换开销
100                     进程之间切换开销大; 线程之间切换开销小(创建和销毁)
101                 创建
102                     进程fork/vfork;    线程pthread_create
103                 销毁
104                     进程结束,它拥有的所有线程都将销毁; 线程结束,不会影响同个进程中的其他线程
105                 私有属性
106                     进程:PCB(进程控制块);  线程:TCB(线程控制块),线程Id,寄存器,上下文
107             一个程序至少只有一个进程,一个进程至少有一个线程
108     启动和终止线程
109         构造线程
110             init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc )
111             父线程就是当前线程(开启多线程的线程),子线程对象是由其父线程来进行空间分配的,继承了parent线程是否为Daemon、优先级、加载资源的contextClassLoader以及可继承的ThreadLocal。还会分配给一个唯一的ID来标识这个child线程。
112             init()运行完毕,线程对象就初始化好了,在堆内存中等待运行
113         构造函数
114             Thread(Runnable target)
115             Thread(Runnable target, String name)
116             因为Thread实现类Runnable,所以可以传递Thread
117                 如果多个new Thread()里面的Runnable是同一个线程对象,那么那个对象的变量时被这几个新的线程共享的变量。
118         启动线程
119             start() 含义:当前线程(即parent线程)同步告知java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程
120             start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用run()方法,也就是使得线程得到运行,启动线程,具有异步执行的效果。而直接调用run方法不是异步而是同步
121             注意:执行start()方法的顺序不代表线程启动的顺序
122             注意:启动一个线程之前,最好起个名字
123             活动状态就是线程已经启动且尚未终止(isAlive为true),start()调用了就是活动状态了
124         理解中断
125             被其他线程中断: interrupt()
126             判断是否被中断:isInterrupted()
127                 测试线程Thread对象是否已经被中断,但是不清除状态标志
128             中断表示复位:Thread.interrupted()
129                 测试当前线程是否已经被中断,执行后具有将状态标志清除为false的功能
130             每次抛出InterruptedException之前,JVM会先将中断标识位清除
131             注意:不论是TERMINATED还是InterruptedException, isInterrupted都返回false
132         安全地终止线程
133             中断方式 Interrupt
134             标识位boolean  true false
135                 当run方法完成后线程终止
136             抛出异常 throw new InterruptedException
137         过期的
138             suspend() 暂停
139                 调用后,线程不会释放已经占有的资源(比如锁),而是占有资源进入睡眠状态,容易引发死锁。
140                 缺点是独占、数据不同步
141             resume()  恢复
142                 缺点是独占、不同步
143             stop()        终止
144                 在终结一个线程时不会保证线程的资源正常释放
145                 使用stop释放锁将会给数据造成不一致的结果
146                 会抛出java.lang.ThreadDeath异常
147                 暴力停止线程
148         其他
149             sleep()
150                 在指定的毫秒内让当前"正在执行的线程"休眠(暂停执行),即this.currentThread()返回的线程
151             currentThread()
152                 构造方法
153                     Thread.currentThread().getName()
154                         main
155                     this.getName()
156                         Thread-0
157                 直接调用run()方法
158                     Thread.currentThread().getName()
159                         main
160                     this.getName()
161                         Thread-0
162                 调用start()方法
163                     Thread.currentThread().getName()
164                         Thread-1 || setName("A")--> A 。
165                     this.getName()
166                         Thread-0
167             isAlive()
168                 线程处于正在运行或准备开始运行的状态就认为线程是存活的, start()之后就是活跃状态了
169                 Thread.sleep()   false
170                 构造方法||直接调用run()方法||调用start()方法
171                     Thread.currentThread().isAlive()
172                         true
173                     this.isAlive()
174                         false
175             yield()
176                 放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上就又获得CPU时间片。
177                 将CPU让给其他资源导致速度变慢
178             优先级
179                 优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A线程是一样的
180                 优先级具有规则性,让CPU尽量将执行资源让给优先级比较高的线程。线程的优先级与代码的执行顺序(start()的先后顺序)无关
181                 优先级具有随机性,优先级高的线程不一定每一次都先执行完
182         有副作用
183     用等待/通知机制代替
184     对象及变量的并发访问
185         synchronized
186             同步方法
187                 方法内的变量为线程安全
188                 实例变量非线程安全
189                 多个对象多个锁
190                     synchronized取得的锁都是对象锁
191                 synchronized方法和锁对象
192                     A线程先持有object对象的Lock锁,B线程可以异步的方式调用object对象中的非synchronized类型的方法。
193                     A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的其他synchronized类型的方法则需要等待,也就是同步。
194                     解决了脏读问题
195                 可重入锁
196                     自己可以再次获得自己的内部锁
197                     当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。如果不可锁重入的话,就会造成死锁。
198                     在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的
199                     可重入锁也支持在父子类继承的环境中
200                         子类完全可以通过可重入锁调用父类的同步方法
201                 出现异常,线程持有的锁会自动释放
202                 同步不具有继承性,还得在子类的方法中添加synchronized关键字
203                 有弊端的,假如A线程调用同步方法执行一个长时间任务,B线程则必须等待比较长的时间,这样的情况可以用synchronized语句块来解决,虽然能实现同步,但会受到阻塞,所以影响运行效率。
204             同步语句块
205                 当两个并发线程访问同一个对象object的synchronized(this)同步代码块时,一段时间内只有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行改代码块
206                 当一个线程访问一个对象object的synchronized(this)同步代码块时,另一个线程仍然可以访问该object对象中的  非synchronized(this)同步代码块
207                     一半同步一半异步
208                 当一个线程访问一个对象object的synchronized(this)同步代码块时,另一个线程对同一个object对象中所有其他synchronized(this)同步代码块的访问将被阻塞,说明synchronized使用的对象监视器是一个
209                 synchronized方法和synchronized(this)同步代码块都是锁定当前对象的
210                 将任意对象作为对象监视器,synchronized(非this对象anyobject)
211                     优点
212                         synchronized(非this对象anyobject)代码块中的程序与同步方法是异步的
213                         不与其他锁this同步方法争抢this锁,可以大大提高运行效率
214                     结论
215                         当多个线程同时执行synchronized(x)同步代码块时呈现同步效果
216                             同步原因,使用了同一个对象监视器
217                         当其他线程执行x对象中synchronized同步方法时呈现同步效果
218                         当其他线程执行x对象方法里面的synchronized(this)同步代码块时呈现同步效果
219                         针对X对象内部的同步方法和同步代码块
220                 多个线程调用同一个方法是随机的
221                     多个线程之间没有固定的顺序,随机的
222             静态同步synchronized方法和synchronized(class)代码块
223                 应用在静态方法上
224                 对当前的 *.java文件对应的Class类进行持锁
225                 注意,一个是对象锁,一个是Class锁,会出现异步的情况,Class锁可以对类的所有对象实例起作用,
226                 synchronized(class)对class上锁后,其他线程只能以同步的方式调用class2的静态同步方法
227                 两个不同的对象,但是静态同步方法还是同步运行
228                 只要对象不变,即使对象的属性被改变,运行的结果还是同步
229             synchronized(string)
230                 String常量池特性,两个线程拥有相同的锁,造成另一个线程不能运行
231                 注意:给string赋值另一个值,锁对象就变了
232                 大多数情况下,同步synchronized代码块都不使用String作为锁对象,而改用new Object()实例化对象
233             多线程死锁
234                 synchronized(lock1){ 
235     synchronized(lock2){}
236 }
237 synchronized(lock2){ 
238     synchronized(lock1){}
239 }
240                 监测方法
241                     1. jps查看运行的线程Run的id
242                     2. jstack -l id
243                     3. DeadThread
244         volatile
245             非原子:i++
246                 解决就是用synchronized或者AtomicInteger原子类
247         volatile v.s. synchronized
248             修饰
249                 volatile只能用于修饰变量
250                 synchronized可以修饰方法以及代码块
251             阻塞
252                 多线程访问volatile不会发生阻塞
253                 多线程访问synchronized会阻塞
254             原子性
255                 volatile不能保证原子性,能保证可见性
256                 synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步
257             作用
258                 volatile解决的是变量在多个线程之间的可见性
259                 synchronized解决的是多个线程之间访问资源的同步性
260         线程安全包含原子性和可见性两个方面,java的同步机制都是围绕这两个方面来保证线程安全的
261         Atomic 原子方法和原则方法虽然都是原子的,但是原子方法和原子方法之间的调用不是原子的,解决这样的问题必须用同步,synchronized
262     线程间通信
263         不使用等待/通知机制实现多个线程之间的通信
264             传统的使用sleep()+while(true)死循环来实现多个线程间的通信
265                 浪费CPU资源
266         volatile & synchronized
267             共享内存和本地内存拷贝的同步更新问题,使得变量不一定能是最新的
268             volatile: 保证所有线程对变量的可见性
269             synchronized:保证线程对变量访问的可见性和排他性,获取monitor; 主要确保多个线程在同一时刻,智能有一个线程处于方法或者同步块中。
270             synchronized
271                 Monitor.Enter--->get监视器Monitor
272                     Enter成功--->锁定对象Object----->Monitor.Exit----->通知同步队列中的线程出队列
273                     Enter失败---->线程进入同步队列SynchronizedQueue----->Monitor.Exit后通知,出队列
274                 对象、监视器、同步队列和执行线程之间的关系
275         等待/通知机制
276             做什么和怎么做解耦,生产者/消费者模式
277             任意java对象所具备的
278             依托于同步机制,目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量作出的修改
279             等待通知机制:线程A调用了对象O的wait()方法进入了等待状态,而线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象的wait()与notify()或notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
280             方法
281                 notify(): 通知在一个对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁。
282                 notyfyAll(): 通知所有等待在该对象上的线程
283                 wait(): 调用该方法的线程进入waiting状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait()方法后,会释放对象的锁。在从wait方法返回前,线程与其他线程竞争重新获得锁。
284                 wait(long): 超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回(没有线程对锁进行唤醒就自动唤醒)。
285                 wait(long, n): 对于超时时间更细粒度的控制,可以达到纳秒。
286             注意
287                 1)使用wait()、notify()、notifyAll()方法都需要先对调用对象加锁。如果没有持有适当的锁,也会抛出IllegalMonitorStateException
288                 在调用condition.await()方法之前必须调用lock.lock()代码获得同步监视器,否则会报错IllegalMonitorStateException异常
289                 2)调用wait()方法后,线程状态由RUNNING变为WAITTING,将锁释放,并将当前线程放到对象的等待队列。WaitingQueue
290                 3)notify()或notifyAll()方法调用后,不会立刻释放锁,等待线程依旧不会从wait()返回,需要等待调用notify()、notifyAll()的线程释放锁之后,等待线程才可能会拿到锁,等待线程才有机会从wait()返回。
291                 4)notify()方法将等待队列WaitingQueue中的一个等待线程从等待队列中移到同步队列SychronizedQueue中,而notifyAll()方法则是将等待队列WaitingQueue中的所有线程全部移动到同步队列SynchronizedQueue中,被移动的线程状态由WAITING变为BLOCKED。
292                 5)从wait()返回的前提是获取调用对象的锁。
293                 6) 执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。
294                 7) wait()方法锁释放,notify()锁不释放,sleep()锁不释放
295                 8) 当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常,导致线程终止,锁也会被释放
296                 实现
297                     synchronized+wait/notify实现等待通知模式
298                         被通知的线程是JVM随机选择的
299                         所有的线程都会注册在一个对象上
300                     condition + lock 实现等待通知模式
301                         支持选择性通知,调度线程上更加灵活
302                         Lock对象里面可以创建多个Condition实例(对象监视器)
303         等待/通知的经典范式
304             等待方(消费者)
305                 原则
306                     加锁:获取对象的锁
307                     循环:如果条件不满足,那么调用对象的wait()方法,被通知后仍然要检查条件。
308                     处理逻辑:条件满足则执行对应的逻辑
309                 伪代码
310                     synchronized(对象) {
311    while(条件不满足) {
312           对象.wait();
313     }
314     对应的处理逻辑
315 }
316             通知方(生产者)
317                 原则
318                     获取对象的锁
319                     改变条件
320                     通知所有等待在对象上的线程notifyAll()
321                 伪代码
322                     synchronized(对象) {
323     改变条件
324    对象.notifyAll();
325 }
326             一个生产者一个消费者
327                 条件不同,一个满足条件,一个不满足条件
328                 伪代码
329                     synchronized(对象) {
330     if(条件满足) {
331           对象.wait();
332     }
333     跟上面条件一致
334     对应的处理逻辑
335     对象.notify();
336 }
337                 操作数
338                     synchronized代码块
339                 操作栈
340                     synchronized方法
341             多生产者多消费者
342                 容易出现假死情况
343                     假死状态的线程都呈WAITING状态
344                     原因:notify唤醒的可能是异类,也可能是同类;比如生产者唤醒生产者
345                     解决:将notify()改成notifyAll(); 不光通知同类,也通知异类
346                     synchronized(对象) {
347     while(条件满足) {
348           对象.wait();
349     }
350     对应的处理逻辑
351     跟上面条件一致
352     对象.notifyAll();
353 }
354                 操作数
355                     synchronized代码块
356                 操作栈
357                     synchronized方法
358         等待超时模式
359             等待超时模式就是在等待/通知范式基础上增加了超时控制,避免执行时间过长,也不会“永久”阻塞调用者,而是按照调用者的要求返回。
360             超时等待:调用一个方法时,等待一段时间(一般给定一个时间段),如果该方法能够在给定的时间段内得到结果,那么将结果立刻返回,反之,超时返回默认结果。
361             伪代码
362                 //对当前对象加锁
363 public synchronized Object get(lon mills) throws InterruptedException{
364     long future = System.currentTimeMillis() + mills;//超时时间
365     long remaining = mills;//等待持续时间
366     //当超时大于0并且result返回值不满足要求则继续等待
367     //当时间到了或者返回结果满足要求则不再等待
368     while((result == null) && remaining > 0){
369         wait(remaining);
370         remaining = future - System.currentTimeMillis();
371     }
372     return result;
373 }
374             应用场景:针对昂贵资源(比如数据库的连接)的获取都应该加以超时限制,是系统的一种自我保护机制
375         管道输入/输出流
376             管道输入输出流与普通的文件输入输出流或者网络输入输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介是内存
377             一个线程发送数据到输出管道,另一个线程从输入管道中读取数据
378             4种具体的实现
379                 PipedOutputStream
380                     write(string.getBytes())
381                 PipedInputStream
382                     read(byte[] byteArray)
383                 PipedReader
384                     read(char[] xx)
385                 PipedWriter
386                     write(string)
387                 面向字节
388                 面向字符
389             注意:out.connect(in);  对于Piped类型的流,必须先要进行绑定,也就是调用connect方法,如果没有将输入/输出流绑定起来,对于该流的访问将会抛出异常
390             读取线程启动后如果没有数据被写入,线程会阻塞在in.read()代码中,直到有数据被写入,才继续往下运行。
391         Thread.join()的使用
392             本质
393                 涉及了等待/通知机制,等待前驱线程结束,接收前驱线程结束通知,源码本质也是wait()和notifyAll()
394                 join方法的作用是等待线程对象销毁
395                 while(isAlive()){
396    wait(0);//表示永远等下去
397 }
398                 指导join线程终止后,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM里实现的,所以在JDK中看不到
399                 CountDownLatch可以实现join的功能,并且比join的功能更多
400             方法
401                 join(): 当前线程A等待 thread线程终止  之后才从  thread.join  返回
402                 join(long millis): 如果在给定的时间内没有终止,那么将会从该超时方法中返回,毫秒
403                 join(long millis,int nanos): 如果在给定的时间内没有终止,那么将会从该超时方法中返回,纳秒
404             join过程中,如果当前线程对象被中断,则当前线程出现InterruptedException异常
405             join v.s. synchronized
406                 共同点
407                     具有使线程排队运行的作用,有点类似同步的运行效果
408                 区别
409                     join在内部使用wait()方法进行等待,具有释放锁的特点
410                     synchronized关键字使用的是 对象监视器原理作为同步
411             join(long) v.s. sleep(long)
412                 join(long)在内部使用wait(long)方法进行等待,具有释放锁的特点
413                 sleep(long)不释放锁
414             注意:join后面的代码提前运行可能会出现陷阱意外,原因在于join方法先运行抢到锁,然后释放锁;之后再次争抢锁,发现时间已经过去,就会释放锁执行后面的代码,导致出现意外。
415         ThreadLocal的使用
416             背景
417                 变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量
418                 threadLocal实现每一个线程都有自己的共享变量
419             ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
420             ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
421             方法
422                 initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。如果set()方法没有调用,第一次get()方法调用时会进行初始化 initialValue(),每个线程会调用一次。
423                 get()方法是用来获取ThreadLocal在当前线程中保存的变量副本
424                 set(T value)用来设置当前线程中变量的副本
425                 remove()用来移除当前线程中变量的副本
426             工作原理
427                 Thread类中有一个成员变量属于ThreadLocalMap类(一个定义在ThreadLocal类中的内部类),它是一个Map,他的key是ThreadLocal实例对象。
428                 当为ThreadLocal类的对象set值时,首先获得当前线程的ThreadLocalMap类属性,然后以ThreadLocal类的对象为key,设定value,值时则类似。
429                 ThreadLocal变量的活动范围为某线程,是该线程“专有的,独自霸占”的,对该变量的所有操作均由该线程完成!也就是说,ThreadLocal 不是用来解决共享对象的多线程访问的竞争问题的,因为ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。当线程终止后,这些值会作为垃圾回收。
430                 由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非是共享的,
431             存储结构的好处
432                 1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
433                 2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
434             弱引用GC
435                 1、使用完线程共享变量后,显示调用remove方法清除线程共享变量可以及时清除
436                 2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
437                 3、对于ThreadLocal变量,我们可以手动的将其置为Null,比如tl =null。那么这个ThreadLocal对应的所有线程的局部变量都有可能被回收。
438             解决Hash冲突方法
439                 线性探测
440             应用场景: 用来解决 数据库连接、Session管理、AOP耗时统计等。
441             注意
442                 不过有点遗憾的是只能放一个值,再次调用set设置值,会覆盖前一次set的值。如果要多个变量,新建多个ThreadLocal对象
443                 是单个线程内函数和组件的共享变量,不是多线程的共享变量,线程隔离
444                 覆盖initialValue()方法可以设置默认初始值,使得get()不会返回null
445                 使用InheritableThreadLocal类可以让子线程从父线程中取得值,如果子线程在取得值得同时,主线程将InheritableThreadLocal中的值进行修改,那么子线程取到的值还是旧值
446     线程池技术
447         好处
448             1. 降低资源消耗
449                 通过重复利用已经创建的线程消除了频繁创建和消亡线程的系统资源开销
450             2. 提高响应速度
451                 当任务到达时,任务可以不需要等等待线程创建就能立即执行
452             3. 提高线程的可管理型
453                 统一分配,调优和监控
454             4. 面对过量任务的提交能够平缓的劣化。
455         实现原理
456             处理流程
457                 1. 提交任务
458                 2. 判断核心线程池是否已满,否 创建线程执行任务,是下一步
459                 3. 判断队列是否已满,否 将任务存储在队列里,是下一步
460                 4. 判断线程池是否已满, 否 创建线程执行任务,是下一步
461                 5. 按照饱和策略处理无法执行的任务
462             工作线程
463                 线程池创建线程时,会将线程封装成工作线程worker,worker在执行完任务之后还会循环获取工作队列里的任务来执行
464         线程池的使用
465             见 ThreadPoolExecutor
466         线程池的本质就是使用了一个线程安全的工作队列连接工作者线程和客户端线程,客户端线程将任务放入工作队列后便返回,而工作者线程则不断从工作队列上取出工作并执行。当工作队列为空时,所有的工作者线程均等待在工作队列上,当有客户端提交了一个任务后会通知任意一个工作者线程,随着大量任务被提交,更多的工作者线程会被唤醒。
467         线程池的数量不是越多越好,具体的数量需要评估每个任务的处理时间,以及当前计算机的处理器能力和数量。使用的线程过少,无法发挥处理器的性能;使用的线程过多,将会增加系统的无故开销,起到相反的作用。
468         应用
469             数据库连接池
470             服务器线程池
471         更多参加java并发框架-Executor框架
472     Timer定时器
473         线程调度任务以供将来在后台线程中执行的功能。 任务可以安排一次执行,或定期重复执行。
474         定时计划任务
475         构造函数
476             Timer():创建一个新的计时器
477             Timer(boolean isDaemon):创建一个新的计时器,可以指定其关联的线程作为守护程序运行
478             Timer(String name):创建一个新的计时器,其关联的线程具有指定的名称
479             Timer(String name,boolean isDaemon):创建一个新的计时器,其关联的线程具有指定的名称,并且可以指定作为守护程序运行
480         这个类是线程安全的:多个线程可以共享一个单独的Timer对象,而不需要外部同步。内部使用多线程的方式进行处理
481         API
482             Timer.cancel()
483                 终止此计时器,将任务队列里的全部任务清空
484                 有时候不一定会停止执行计划任务,而是正常执行,因为有时候并没有抢到queue锁
485             TimerTask.cancel()
486                 将自身从任务队列里清除,其他任务不受影响
487             purge()
488                 从该计时器的任务队列中删除所有取消的任务。
489             schedule(TimerTask task, Date time)
490                 在指定的日期,执行一次某任务
491             schedule(TimerTask task, Date firstTime, long period)
492                 从指定的时间开始 ,按照执行的间隔周期性地无限循环地执行某一任务。
493                     执行任务的时间晚于当前时间,在未来执行
494                     执行任务的时间早于当前时间,立即执行
495             schedule(TimerTask task, long delay)
496                 以当前时间为参考时间,延迟指定的毫秒数后执行一次制定任务
497             schedule(TimerTask task, long delay, long period)
498                 以当前时间为参考时间,延迟指定的毫秒数后执行一次制定任务,再以某一个时间间隔无限次数地执行制定任务
499             scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
500                 从指定的时间 开始 ,对指定的任务执行重复的 固定周期执行 。
501             scheduleAtFixedRate(TimerTask task, long delay, long period)
502                 以当前时间为参考时间,在指定的延迟之后 开始 ,重新执行 固定周期的指定任务。
503         schedule V.S. scheduleAtFixedRate
504             延时
505                 下一次任务的执行时间参考的是上一次任务的“结束”时的时间计算
506             不延时
507                 schedule
508                     下一次任务的执行时间参考的是上一次任务的“开始”时的时间计算
509                 scheduleAtFixedRate
510                     下一次任务的执行时间参考的是上一次任务的“结束”时的时间计算
511             追赶性
512                 schedule
513                     执行任务的时间早于当前时间,立即执行, 不填充
514                 scheduleAtFixedRate
515                     执行任务的时间早于当前时间,立即执行, 不补充
516         Timer中允许有多个TimerTask任务,以队列的形式被顺序执行,所以执行的时间和预期的时间不一致,因为前面的任务有可能消耗的时间太长,后面的任务运行的时间也会被延迟
517         Java 5.0引入了java.util.concurrent软件包,其中一个java.util.concurrent程序是ScheduledThreadPoolExecutor ,它是用于以给定速率或延迟重复执行任务的线程池。 这实际上是对一个更灵活的替代Timer / TimerTask组合,因为它允许多个服务线程,接受各种时间单位,并且不需要子类TimerTask (只实现Runnable )。 使用一个线程配置ScheduledThreadPoolExecutor使其等同于Timer 。
518     单例模式
519         立即加载/饿汉模式
520             静态实例初始化
521 私有构造方法
522 静态getInstance
523         延迟加载/懒汉模式
524             静态实例声明
525 私有构造方法
526 静态getInstance{
527    if(是null){ 
528         初始化  
529     }
530 }
531             解决
532                 声明synchronized关键字
533                 尝试同步代码块
534                 针对重要代码进行单独的同步
535                 使用DCL双检查锁机制
536                 使用静态内置类实现单例模式
537                 序列化和反序列化的单例模式实现
538                     readResolve()
539                 使用static代码块来实现单例模式
540                 使用enum枚举数据类型实现单例模式
541     SimpleDateFormat
542         负责日期的转换和格式化
543         非线程安全的
544         解决方法
545             每个线程一个SimpleDateFormat实例
546             ThreadLocal类
View Code

 

10. 参考网址

  1. 参考来源:http://cmsblogs.com/wp-content/resources/img/sike-juc.png
  2. 《Java并发编程的艺术》_方腾飞PDF 提取码:o9vr
  3. http://ifeve.com/the-art-of-java-concurrency-program-1/
  4. Java并发学习系列-绪论
  5. Java并发编程实战
  6. 死磕 Java 并发精品合集

 

posted @ 2019-07-17 11:31  海米傻傻  阅读(402)  评论(0编辑  收藏  举报