网站建设需要考啥证,设计 网站 现状,建设银行网站app,莱芜都市网下载多线程与线程池 文章目录 多线程与线程池1. 相关概念1.1 线程调度1.2 守护线程 2. 生命周期3. 同步机制/同步锁3.1 synchronized3.2 lock3.3 synchronized 与 Lock 的对比 4. 死锁5. 线程通信5.1 线程间的通信5.2 等待唤醒机制5.3 举例5.4 调用 wait 和 notify 需注意的细节5.5…多线程与线程池 文章目录 多线程与线程池1. 相关概念1.1 线程调度1.2 守护线程 2. 生命周期3. 同步机制/同步锁3.1 synchronized3.2 lock3.3 synchronized 与 Lock 的对比 4. 死锁5. 线程通信5.1 线程间的通信5.2 等待唤醒机制5.3 举例5.4 调用 wait 和 notify 需注意的细节5.5 生产者消费者问题 6. 线程池6.1七大核心属性6.2线程池处理流程图6.2.1 线程池处理6.2.2 addWorker方法 1. 相关概念
1.1 线程调度
分时调度所有线程轮流使用 CPU 的使用权并且平均分配每个线程占用 CPU 的时间。抢占式调度让优先级高的线程以较大的概率优先使用 CPU。如果线程的优先级相同那么会随机选择一个(线程随机性)Java 使用的为抢占式调度。
每个线程都有一定的优先级同优先级线程组成先进先出队列先到先服务使用分时调度策略。优先级高的线程采用抢占式策略获得较多的执行 机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级。 Thread 类的三个优先级常量 MAX_PRIORITY(10)最高优先级MIN _PRIORITY (1)最低优先级NORM_PRIORITY (5)普通优先级默认情况下 main 线程具有普通优先级
1.2 守护线程
有一种线程它是在后台运行的它的任务是为其他线程提供服务的这种线程被称为“守护线程”。JVM 的垃圾回收线程就是典型的守护线程。守护线程有个特点就是如果所有非守护线程都死亡那么守护线程自动死亡。形象理解兔死狗烹鸟尽弓藏
2. 生命周期
在 java.lang.Thread.State 的枚举类中这样定义
public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
}NEW新建线程刚被创建但是并未启动。还没调用 start 方法。 RUNNABLE可运行这里没有区分就绪和运行状态。因为对于 Java 对象来说只能标记为可运行至于什么时候运行不是 JVM 来控制的了是 OS 来进行调度的而且时间非常短暂因此对于 Java 对象的状态来说无法区分。 Teminated被终止表明此线程已经结束生命周期终止运行。 BLOCKED锁阻塞在 API 中的介绍为一个正在阻塞、等待一个监视器锁锁对象的线程处于这一状态。只有获得锁对象的线程才能有执行机会。 比如线程 A 与线程 B 代码中使用同一锁如果线程 A 获取到锁线程 A 进入到 Runnable 状态那么线程 B 就进入到 Blocked锁阻塞状态。 TIMED_WAITING计时等待在 API 中的介绍为一个正在限时等待另一个线程执行一个唤醒动作的线程处于这一状态。 当前线程执行过程中遇到 Thread 类的 sleep 或 joinObject 类的 waitLockSupport 类的 park 方法并且在调用这些方法时设置了时间那么当前线程会进入 TIMED_WAITING直到时间到或被中断。 WAITING无限等待在 API 中介绍为一个正在无限期等待另一个线程执行一个特别的唤醒动作的线程处于这一状态。 当前线程执行过程中遇到遇到 Object 类的 waitThread 类的joinLockSupport 类的 park 方法并且在调用这些方法时没有指定时间那么当前线程会进入 WAITING 状态直到被唤醒。
说明当从 WAITING 或 TIMED_WAITING 恢复到 Runnable 状态时如果发现当前线程没有得到监视器锁那么会立刻转入 BLOCKED 状态。 3. 同步机制/同步锁
3.1 synchronized
同步锁对象可以是任意类型但是必须保证竞争“同一个共享资源”的多个线程必须使用同一个“同步锁对象”。
3.2 lock 保证线程的安全。与采用 synchronized 相比Lock 可提供多种锁方案更灵活、更强大。Lock 通过显式定义同步锁对象来实现同步。同步锁使用Lock 对象充当。 java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问每次只能有一个线程对 Lock 对象加锁线程开始访问共享资源之前应先获得 Lock 对象。 在实现线程安全的控制中比较常用的是 ReentrantLock可以显式加锁、释放锁。 ReentrantLock 类实现了 Lock 接口它拥有与 synchronized 相同的并发性和内存语义但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外它还提供了在激烈争用情况下更佳的性能。
3.3 synchronized 与 Lock 的对比 Lock 是显式锁手动开启和关闭锁别忘记关闭锁synchronized 是隐式锁出了作用域、遇到异常等自动解锁 Lock 只有代码块锁synchronized 有代码块锁和方法锁 使用 Lock 锁JVM 将花费较少的时间来调度线程性能更好。并且具有更好的扩展性提供更多的子类读写锁等更体现面向对象。
说明开发建议中处理线程安全问题优先使用顺序为Lock ---- 同步代码块 ---- 同步方法
4. 死锁
同步机制带来的死锁问题不同的线程分别占用对方需要的同步资源不放弃都在等待对方放弃自己需要的同步资源就形成了线程的死锁。
互斥条件进程要求对所分配的资源进行排它性控制即在一段时间内某资源仅为一进程所占用。请求和保持条件当进程因请求资源而阻塞时对已获得的资源保持不放。不剥夺条件进程已获得的资源在未使用完之前不能剥夺只能在使用完时由自己释放。循环等待存在一个进程或线程的资源申请序列使得每个进程或线程都在等待下一个进程或线程所持有的资源。
解决死锁死锁一旦出现基本很难人为干预只能尽量规避。可以考虑打破上面的诱发条件。
针对互斥条件互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题。针对请求和保持条件可以考虑一次性申请所有所需的资源这样就不存在等待的问题。针对不剥夺条件占用部分资源的线程在进一步申请其他资源时如果申请不到就主动释放掉已经占用的资源。针对循环等待可以将资源改为线性顺序。申请资源时先申请序号较小的这样避免循环等待问题。
5. 线程通信
5.1 线程间的通信
当我们需要多个线程来共同完成一件任务并且我们希望他们有规律的执行那么多线程之间需要一些通信机制可以协调它们的工作以此实现多线程共同操作一份数据。
比如线程 A 用来生产包子的线程 B 用来吃包子的包子可以理解为同一资源线程 A 与线程 B 处理的动作一个是生产一个是消费此时 B 线程必须等到 A 线程完成后才能执行那么线程 A 与线程 B 之间就需要线程通信即—— 等待唤醒机制。
5.2 等待唤醒机制
这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争race比如去争夺锁但这并不是故事的全部线程间也会有协作机制。在一个线程满足某个条件时就进入等待状态wait() / wait(time) 等待其他线程执行完他们的指定代码过后再将其唤醒notify();或可以指定wait 的时间等时间到了自动唤醒在有多个线程进行等待时如果需要可以使用 notifyAll()来唤醒所有的等待线程。wait/notify 就是线程间的一种协作机制。
wait线程不再活动不再参与调度进入 wait set 中因此不会浪费 CPU 资源也不会去竞争锁了这时的线程状态是 WAITING 或 TIMED_WAITING。它 还要等着别的线程执行一个特别的动作也即“通知notify”或者等待时间到在这个对象上等待的线程从 wait set 中释放出来重新进入到调度队ready queue中notify则选取所通知对象的 wait set 中的一个线程释放notifyAll则释放所通知对象的 wait set 上的全部线程。
注意被通知的线程被唤醒后也不一定能立即恢复执行因为它当初中断的地方是在同步块内而此刻它已经不持有锁所以它需要再次尝试去获取锁很可能面临其它线程的竞争成功后才能在当初调用 wait 方法之后的地方恢复执行。
如果能获取锁线程就从 WAITING 状态变成 RUNNABLE可运行状态否则线程就从 WAITING 状态又变成 BLOCKED等待锁 状态
注意在JUC中将会学到更多有关等待唤醒机制的类与方法建议使用JUC中学习到的
5.3 举例
例题使用两个线程打印 1-100。线程 1, 线程 2 交替打印
class Communication implements Runnable {int i 1;public void run() {while (true) {synchronized (this) {notify();if (i 100) {System.out.println(Thread.currentThread().getName() : i);} else break;try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}
}5.4 调用 wait 和 notify 需注意的细节
wait 方法与 notify 方法必须要由同一个锁对象调用。因为对应的锁对象可以通过 notify 唤醒使用同一个锁对象调用的 wait 方法后的线程。wait 方法与 notify 方法是属于 Object 类的方法的。因为锁对象可以是任意对象而任意对象的所属类都是继承了 Object 类的。wait 方法与 notify 方法必须要在同步代码块或者是同步函数中使用。因为必须要通过锁对象调用这 2 个方法。否则会报 java.lang.IllegalMonitorStateException 异常。
5.5 生产者消费者问题
等待唤醒机制可以解决经典的“生产者与消费者”的问题。生产者与消费者问题英语Producer-consumer problem也称有限缓冲问题英语Bounded-buffer problem是一个多线程同步问题的经典案例。该问题描述了两个多个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。
生产者的主要作用是生成一定量的数据放到缓冲区中然后重复此过程。与此同时消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据消费者也不会在缓冲区中空时消耗数据。
举例
生产者(Productor)将产品交给店员(Clerk)而消费者(Customer)从店员处取走产品店员一次只能持有固定数量的产品(比如:20如果生产者试图生产更多的产品店员会叫生产者停一下如果店中有空位放产品了再通知生产者继续生产如果店中没有产品了店员会告诉消费者等一下如果店中有产品了再通知消费者来取走产品。
生产者与消费者问题中其实隐含了两个问题
线程安全问题因为生产者与消费者共享数据缓冲区产生安全问题。不过这个问题可以使用同步解决。线程的协调工作问题要解决该问题就必须让生产者线程在缓冲区满时等待(wait)暂停进入阻塞状态等到下次消费者消耗了缓冲区中的数据的时候通知(notify)正在等待的线程恢复到就绪状态重新开始往缓冲区添加数据。同样也可以让消费者线程在缓冲区空时进入等待(wait)暂停进入阻塞状态等到生产者往缓冲区添加数据之后再通知(notify)正在等待的线程恢复到就绪状态。通过这样的通信机制来解决此类问题。
代码实现
public class ConsumerProducerTest {public static void main(String[] args) {Clerk clerk new Clerk();Producer p1 new Producer(clerk);Consumer c1 new Consumer(clerk);Consumer c2 new Consumer(clerk);p1.setName(生产者 1);c1.setName(消费者 1);c2.setName(消费者 2);p1.start();c1.start();c2.start();}
}//生产者
class Producer extends Thread{private Clerk clerk;public Producer(Clerk clerk){this.clerk clerk;}Overridepublic void run() {System.out.println(生产者开始生产产品);while(true){try {Thread.sleep(40);} catch (InterruptedException e) {e.printStackTrace();}//要求 clerk 去增加产品clerk.addProduct();}}
}//消费者
class Consumer extends Thread{private Clerk clerk;public Consumer(Clerk clerk){this.clerk clerk;}Overridepublic void run() {System.out.println(消费者开始消费产品);while(true){try {Thread.sleep(90);} catch (InterruptedException e) {e.printStackTrace();}//要求 clerk 去减少产品clerk.minusProduct();}}
}//资源类缓冲区
class Clerk {private int productNum 0;//产品数量private static final int MAX_PRODUCT 20;private static final int MIN_PRODUCT 1;//增加产品public synchronized void addProduct() {if(productNum MAX_PRODUCT){productNum;System.out.println(Thread.currentThread().getName() 生产了第 productNum 个产品);//唤醒消费者this.notifyAll();}else{try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}//减少产品public synchronized void minusProduct() {if(productNum MIN_PRODUCT){System.out.println(Thread.currentThread().getName() 消费了第 productNum 个产品);productNum--;//唤醒生产者this.notifyAll();}else{try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}
}6. 线程池
如果并发的线程数量很多并且每个线程都是执行一个时间很短的任务就结束了这样频繁创建线程就会大大降低系统的效率因为频繁创建线程和销毁线程需要的代价较高。
思路提前创建好多个线程放入线程池中使用时直接获取使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
注意线程资源必须通过线程池提供不允许在应用中自行显示创建线程。
引用阿里《Java开发手册》中的一段描述
【强制】线程池不允许使用Executors创建建议通过ThreadPoolExecutor的方式创建这样的处理方式让写的同学更加明确线程池的运行规则规避资源耗尽的风险。
说明Executors返回的线程池对象的弊端如下
1.FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integet.MAX_VALUE,可能会堆积大量的请求从而导致OOM;
2.CachedThreadPool:
允许创建线程数量为Integet.MAX_VALUE,可能会创建大量的线程从而导致OOM.
6.1七大核心属性
corePoolSize(int)核心线程数量。默认情况下线程池中的线程数为0当有任务来之后就会创建一个线程去执行任务当线程池中的线程数目达到corePoolSize后就会把到达的任务放到任务队列当中。线程池将长期保证这些线程处于存活状态即使线程已经处于闲置状态。除非配置了allowCoreThreadTimeOuttrue核心线程数的线程也将不再保证长期存活于线程池内在空闲时间超过keepAliveTime后被销毁。workQueue阻塞队列存放等待执行的任务线程从workQueue中取任务若无任务将阻塞等待。当线程池中线程数量达到corePoolSize后就会把新任务放到该队列当中。JDK提供了四个可直接使用的队列实现分别是基于数组的有界队列ArrayBlockingQueue、基于链表的无界队列LinkedBlockingQueue、只有一个元素的同步队列SynchronousQueue、优先级队列PriorityBlockingQueue。在实际使用时一定要设置队列长度。maximumPoolSize(int)线程池内的最大线程数量线程池内维护的线程不得超过该数量大于核心线程数量小于最大线程数量的线程将在空闲时间超过keepAliveTime后被销毁。当阻塞队列存满后将会创建新线程执行任务线程的数量不会大于maximumPoolSize。keepAliveTime(long)线程存活时间若线程数超过了corePoolSize线程闲置时间超过了存活时间该线程将被销毁。除非配置了allowCoreThreadTimeOuttrue核心线程数的线程也将不再保证长期存活于线程池内在空闲时间超过keepAliveTime后被销毁。TimeUnit unit线程存活时间的单位例如TimeUnit.SECONDS表示秒。RejectedExecutionHandler拒绝策略当任务队列存满并且线程池个数达到maximunPoolSize后采取的策略。ThreadPoolExecutor中提供了四种拒绝策略分别是抛RejectedExecutionException异常的AbortPolicy(默认策略)、使用调用者所在线程来运行任务CallerRunsPolicy、丢弃一个等待执行的任务然后尝试执行当前任务DiscardOldestPolicy、不动声色的丢弃并且不抛异常DiscardPolicy。项目中如果为了更多的用户体验可以自定义拒绝策略。threadFactory创建线程的工厂虽说JDK提供了线程工厂的默认实现DefaultThreadFactory但还是建议自定义实现最好这样可以自定义线程创建的过程例如线程分组、自定义线程名称等以进行线程监控。
6.2线程池处理流程图
6.2.1 线程池处理 拒绝策略RejectedExecutionHandler
当任务队列和线程池都满了时所采取的应对策略默认是AbordPolicy表示无法处理新任务并抛出RejectedExecutionException异常。此外还有3种策略 CallerRunsPolicy用调用者所在的线程处理任务。此策略提供简单的反馈机制能够减缓新任务的提交速度。DiscardPolicy不能执行任务并将任务删除。DiscardOldestPolicy丢弃队列最近的任务并执行当前的任务。 6.2.2 addWorker方法
该方法返回false则会执行拒绝策略方法
主流程图 流程中去除一些异常情况只留了主要流程流程中有一步验证线程数大于核心线程或者最大线程数如果传递的参数core等于true那么运行线程数量不能大于核心线程数量如果为false则当前线程数量不能大于最大。
addWorker只有两个作用增加工作线程数量、创建一个Worker并加到工作线程集合中。 文章转载自: http://www.morning.aiai201.cn.gov.cn.aiai201.cn http://www.morning.lzqdl.cn.gov.cn.lzqdl.cn http://www.morning.nzklw.cn.gov.cn.nzklw.cn http://www.morning.qttg.cn.gov.cn.qttg.cn http://www.morning.rqkk.cn.gov.cn.rqkk.cn http://www.morning.jjxnp.cn.gov.cn.jjxnp.cn http://www.morning.tscsd.cn.gov.cn.tscsd.cn http://www.morning.zkjqj.cn.gov.cn.zkjqj.cn http://www.morning.kpgms.cn.gov.cn.kpgms.cn http://www.morning.tpdg.cn.gov.cn.tpdg.cn http://www.morning.burpgr.cn.gov.cn.burpgr.cn http://www.morning.kgmkl.cn.gov.cn.kgmkl.cn http://www.morning.yhtnr.cn.gov.cn.yhtnr.cn http://www.morning.kkjlz.cn.gov.cn.kkjlz.cn http://www.morning.hrypl.cn.gov.cn.hrypl.cn http://www.morning.zmlnp.cn.gov.cn.zmlnp.cn http://www.morning.qpsft.cn.gov.cn.qpsft.cn http://www.morning.nmngg.cn.gov.cn.nmngg.cn http://www.morning.cylbs.cn.gov.cn.cylbs.cn http://www.morning.bxqry.cn.gov.cn.bxqry.cn http://www.morning.wxlzr.cn.gov.cn.wxlzr.cn http://www.morning.rbktw.cn.gov.cn.rbktw.cn http://www.morning.hdqqr.cn.gov.cn.hdqqr.cn http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn http://www.morning.qtyfb.cn.gov.cn.qtyfb.cn http://www.morning.lwsct.cn.gov.cn.lwsct.cn http://www.morning.snmth.cn.gov.cn.snmth.cn http://www.morning.xdwcg.cn.gov.cn.xdwcg.cn http://www.morning.ldcrh.cn.gov.cn.ldcrh.cn http://www.morning.djmdk.cn.gov.cn.djmdk.cn http://www.morning.enjoinfo.cn.gov.cn.enjoinfo.cn http://www.morning.hbnwr.cn.gov.cn.hbnwr.cn http://www.morning.bwzzt.cn.gov.cn.bwzzt.cn http://www.morning.youngbase.cn.gov.cn.youngbase.cn http://www.morning.ynlbj.cn.gov.cn.ynlbj.cn http://www.morning.tyklz.cn.gov.cn.tyklz.cn http://www.morning.whnps.cn.gov.cn.whnps.cn http://www.morning.przc.cn.gov.cn.przc.cn http://www.morning.ey3h2d.cn.gov.cn.ey3h2d.cn http://www.morning.fhxrb.cn.gov.cn.fhxrb.cn http://www.morning.wqsjx.cn.gov.cn.wqsjx.cn http://www.morning.ndmbz.cn.gov.cn.ndmbz.cn http://www.morning.fbxdp.cn.gov.cn.fbxdp.cn http://www.morning.lbcbq.cn.gov.cn.lbcbq.cn http://www.morning.rtzd.cn.gov.cn.rtzd.cn http://www.morning.mfbzr.cn.gov.cn.mfbzr.cn http://www.morning.cknws.cn.gov.cn.cknws.cn http://www.morning.grlth.cn.gov.cn.grlth.cn http://www.morning.fylsz.cn.gov.cn.fylsz.cn http://www.morning.rmlz.cn.gov.cn.rmlz.cn http://www.morning.hmnhp.cn.gov.cn.hmnhp.cn http://www.morning.rksnk.cn.gov.cn.rksnk.cn http://www.morning.ktsth.cn.gov.cn.ktsth.cn http://www.morning.mzhh.cn.gov.cn.mzhh.cn http://www.morning.lrjtx.cn.gov.cn.lrjtx.cn http://www.morning.cmrfl.cn.gov.cn.cmrfl.cn http://www.morning.yxkyl.cn.gov.cn.yxkyl.cn http://www.morning.gwdnl.cn.gov.cn.gwdnl.cn http://www.morning.ddjp.cn.gov.cn.ddjp.cn http://www.morning.mzkn.cn.gov.cn.mzkn.cn http://www.morning.cmhkt.cn.gov.cn.cmhkt.cn http://www.morning.rsdm.cn.gov.cn.rsdm.cn http://www.morning.rbkml.cn.gov.cn.rbkml.cn http://www.morning.mwlxk.cn.gov.cn.mwlxk.cn http://www.morning.tllws.cn.gov.cn.tllws.cn http://www.morning.rlxg.cn.gov.cn.rlxg.cn http://www.morning.rdsst.cn.gov.cn.rdsst.cn http://www.morning.jpgfx.cn.gov.cn.jpgfx.cn http://www.morning.lwcgh.cn.gov.cn.lwcgh.cn http://www.morning.tlfzp.cn.gov.cn.tlfzp.cn http://www.morning.qdsmile.cn.gov.cn.qdsmile.cn http://www.morning.hqmfn.cn.gov.cn.hqmfn.cn http://www.morning.ksbmx.cn.gov.cn.ksbmx.cn http://www.morning.hbfqm.cn.gov.cn.hbfqm.cn http://www.morning.nbybb.cn.gov.cn.nbybb.cn http://www.morning.frpb.cn.gov.cn.frpb.cn http://www.morning.hryhq.cn.gov.cn.hryhq.cn http://www.morning.mnbcj.cn.gov.cn.mnbcj.cn http://www.morning.lsyk.cn.gov.cn.lsyk.cn http://www.morning.srbfz.cn.gov.cn.srbfz.cn