五、多线程
知识点总结
-
程序、进程与线程
• 程序Program: 是一段静态的代码(指令集合),它是应用程序执行的蓝本。
• 进程Process:指一种正在运行的程序,有自己的地址空间。当程序进入内存运行时,即变成一个进程,是运行中的程序,具有一定的独立功能,是系统资源分配和调度的一个独立单位。
• 线程Thread:进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。线程又被称为轻量级进程(lightweight process) • 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程。 -
并发和并行的区别:其一: 多个CPU同时执行多个任务;其二:一个CPU(采用时间片)同时执行。
-
进程的特点:动态性 、并发性、独立性
-
线程特点
• 轻量级进程
• 独立调度的基本单位
• 可并发执行
• 共享进程资源 -
线程的执行是抢占式的,任何时候可能被挂起,以便另一个线程可以运行,线程的调度和管理由进程本身负责完成。
-
线程的创建
• 方式1:继承Java.lang.Thread类,并覆盖run() 方法
• 方式2:实现Java.lang.Runnable接口,并实现run() 方法 -
两种线程创建方式的比较
• 继承Thread类方式的多线程
优势:编写简单
劣势:无法继承其它父类
• 实现Runnable接口方式的多线程
优势:可以继承其它类,多线程可共享同一个Runnable对象
劣势:编程方式稍微复杂,如果需要访问当前线程,需要调Thread.currentThread()方法
实现Runnable接口方式要通用一些。 -
同步监视器
• synchronized (obj){ }中的obj称为同步监视器
• 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器
• 同步方法中无需指定同步监视器,因为同步方法的同步监视器是this,也就是该对象本身。 -
同步监视器的执行过程
• 第一个线程访问,锁定同步监视器,执行其中代码
• 第二个线程访问,发现同步监视器被锁定,无法访问
• 第一个线程访问完毕,解锁同步监视器
• 第二个线程访问,发现同步监视器未锁,锁定并访问 -
线程执行同步代码块或同步方法时,程序调用Thread.sleep() Thread.yield()方法停止当前线程执行,不会释放同步监视器。
-
Lock锁
• JDK1.5后新增功能,线程开始访问共享资源前应先获得Lock对象。与采用synchronized相比,lock可提供多种锁方案,更灵活
• java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
• ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义, 但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。
• 注意:如果同步代码有异常,要将unlock()写入finally语句块 -
Lock和synchronized的区别
• 1.Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁
• 2.Lock只有代码块锁,synchronized有代码块锁和方法锁
• 3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) -
优先使用顺序:
• Lock----同步代码块(已经进入了方法体,分配了相应资源)----同步方法(在方法体之外) -
线程通信
• 应用场景:生产者和消费者问题
• 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
• 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
• 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
-
Cindition控制线程通信:使用Condition可以让已经得到Lock对象无法继续执行的线程释放Lock对象,Condition对象可以唤醒其他属于等待的线程。
-
常见方法:await()等待 signal()唤醒单个线程 signalAll()唤醒所有等待的线程
常见代码如下:
private final Lock lock=new ReentrantLock();
//获得指定Lock对象对应的Condition
private final Condition cond=lock.newCindition();
- 使用阻塞队列(BlockingQueue)控制线程:线程同步的工具。例如:
BlockingQueue<String> bq=new ArrayBlockingQueue<>(2);
bq.put("java");
bq.put("sad");
//阻塞线程
bq.put("da");
- 线程组
• 线程组表示一个线程的集合。
• 线程组也可以包含其他线程组。线程组构成一棵树。在树中,除了初始线程组外,每个线程组都有一个父线程组。
• 顶级线程组名system,线程的默认线程组名称是main
• 在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组 - 线程组的作用
• 统一管理:便于对一组线程进行批量管理线程或线程组对象
• 安全隔离:允许线程访问有关自己的线程组的信息,但是不允许它访问有关其线程组的父线程组或其他任何线程组的信息 - 什么是线程池
• 创建和销毁对象是非常耗费时间的
• 创建对象:需要分配内存等资源
• 销毁对象:虽然不需要程序员操心,但是垃圾回收器会在后台一直跟踪并销毁
• 对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
• 思路:创建好多个线程,放入线程池中,使用时直接获取引用,不使用时放回池中。可以避免频繁创建销毁、实现重复利用 - 线程池的好处
• 提高响应速度(减少了创建新线程的时间)
• 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
• 提高线程的可管理性:避免线程无限制创建、从而销耗系统资源,降低系统稳定性,甚至内存溢出或者CPU耗尽 - 线程池的应用场合
• 需要大量线程,并且完成任务的时间端
• 对性能要求苛刻
• 接受突发性的大量请求 - • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
• Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
• Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
• Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
• Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
- 线程相关类:ThreadLocal类:代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。
- 通常建议:如果多个线程之间共享资源以达到通信的功能,就使用同步机制;如果仅仅隔离多个线程之间的共享冲突,就可以使用ThreadLocal。
六、正则表达式
知识点总结