Chapter7 多线程
7.1 基本概念
1. 线程与进程
- 进程:程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。 ——生命周期
- 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
2. 并行与并发
- 并行
多个CPU同时执行多个任务 - 并发
一个CPU(采用时间片)同时执行多个任务
3. 多线程优点
- 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
- 提高计算机系统CPU的利用率
- 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
7.2 线程的创建与使用
java.lang.Thread
类
1. 多线程的创建——方法一
步骤
- 创建
Thread
类的子类 - 重写
run()
方法,将此线程操作声明在run()
方法中 - 实例化该子类
- 通过对象调用
start()
方法
- 创建
注意事项
- 多个线程则创建多个对象(若内容不同则需创建多个子类)
- 可以使用匿名子类的方式:
new Thread(){ @overwrite run() { //方法体 } }.start()
Thread
常用方法currentThread()
:静态方法,返回执行当前代码的线程getName()
:获取当前进程名字setName()
:设置当前进程名字yield()
:主动释放当前CPU执行(有时可能下一刻可能又分配到CPU,看上去没有效果)join()
:在当前线程中调用其他线程该方法,则该线程进入阻塞状态,知道另一个线程执行完结束stop()
:强制结束当前线程(已过时)sleep(long millitime)
:让当前线程睡眠指定毫秒数(在run中使用)isalive()
:判断当前线程是否存活
线程优先级
- 相关常量
MAX_PRIORITY: 10 MIN_PRIORITY: 1 NORM_PRIORITY: 5
- 如何设置
getPriority()
:获取线程优先级setPriority(int p)
:设置线程优先级
- 说明:
高优先级线程先执行的概率大,但不一定
- 相关常量
2. 多线程的创建——方法二
- 步骤
- 创建实现了
Runnable
接口的类 - 实现类中实现
run()
方法 - 实例化实现类
- 将对象作为参数创建
Thread
类的对象 - 通过Thread类的对象调用
start
方法
- 创建实现了
- 说明
- 可以用同一个实现类对象创建多个Thread对象
3. 两种方式对比
- 实际开发中
优先选择方式二(实现Runnable
接口的方式) - 不同点
- 实现Runnable接口的方式(方式二)没有单继承的局限性
- 方式二更适合多线程共享数据(同一个对象创建线程)
- 相同点
两种方式都要重写run()
方法,将要执行的代码声明在该方法中
7.3 线程的生命周期
如图:
7.4 线程的同步
通过同步机制解决线程安全问题
1. 同步代码块
- 代码示例
synchronized(同步监视器){ //需要被同步的代码 }
- 说明
- 操作共享数据的代码即为需要被同步的代码
- 共享数据:多个线程共同操作的变量
- 同步监视器:俗称锁,任何类的对象都可以充当锁,多个线程必需共用同一把锁。
- 对于实现Runnable接口的多线程,可以使用当前对象作为锁(
this
) - 对于继承Thread的方式,则需创建静态对象,或使用
当前类名.class
(类也是对象)
- 特点
- 解决了线程安全问题
- 执行同步操作时,只有一个线程操作,其他在等待,相当于单线程
2. 同步方法
synchronized还可以放在方法声明中,表示整个方法为同步方法
继承Thread方式
public synchronized void show (String name){ // }
此时同步监视器为当前类
this
实现接口方式
private static synchronized void show(){//同步监视器:当前类.class // }
方法为静态方法,此时同步监视器为当前类本身
线程死锁问题
死锁
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
解决方法
- 专门的算法、原则
- 尽量减少同步资源的定义
- 尽量避免嵌套同步
3. Lock(锁)
- 步骤
- 实例化ReentrantLock
- 调用lock()方法,手动启动同步
- 调用unlock()方法,结束同步
- 代码示例
private ReenTrantLock lock= new ReenTrantLock() //在run中调用 ··· lock.lock; // 同步内容 lock.unlock;
- 注意事项
- 对于继承方式,lock要声明为静态
- 使用中:Lock>同步代码块>同步方法
7.5 线程的通信
- 涉及到的方法
wait()
: 当前线程进入阻塞状态并释放同步监视器notify()
:唤醒被wait的线程,如果有多个则唤醒优先级高的notifyAll()
:唤醒所有被wait的线程
- 说明
- 上述三个方法必须使用中同步代码块或同步方法中
- 三个方法的调用者,必须是同步监视器
- 三个方法定义在Object类中
sleep()
与wait()
- 都会使当前线程进入阻塞状态
- 声明位置不同,
sleep
声明在Thread
类中 - 调用位置不同,
sleep
可以在任何需要的地方调用,wait
必须使用中同步代码块或同步方法中 - 都用在同步代码块或方法中时,
sleep
不会释放监视器(锁),wait
会释放
7.6 JDK5.0 新增线程创建方式
1. 实现Callable
接口
- 步骤
- 创建
Callable
接口的实现类 - 实现
call
方法,将此线程需要的操作声明在其中 - 创建实现类的对象
- 将对象作为参数传入FutureTask构造器中,创建
FutureTask
类的对象 - 将
FutureTask
类的对象作为参数创建Thread类的对象,并调用start
方法 FutureTask对象.get()
方法可以获取call方法的返回值
- 创建
- 说明
- call方法有返回值
- call方法可以抛出异常,并被外面的操作捕获
- Callable支持泛型
- 代码示例
public class ThreadNew { public static void main(String[] args) { NumThread numThread = new NumThread(); FutureTask futureTask = new FutureTask(numThread); new Thread(futureTask).start(); try{ Object sum = futureTask.get(); System.out.println(sum); }catch (InterruptedException e){ e.printStackTrace(); }catch (ExecutionException e){ e.printStackTrace(); } } } // 自定义实现类 class NumThread implements Callable{ @Override public Object call() throws Exception { // 线程内容,可以有返回值 } }
2. 使用线程池
- 使用
public class ThreadPool { public static void main(String[] args) { //1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置线程池的属性 // ExecutorService 是接口,ThreadPoolExecutor是其实现类,service为其对象 // service1.setCorePoolSize(15); // service1.setKeepAliveTime(); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 service.execute(new NumberThread());//适用于Runnable service.execute(new NumberThread1());//适用于Runnable // service.submit(Callable callable);//适用于Callable //3.关闭连接池 service.shutdown(); } } class NumberThread implements Runnable{ @Override public void run() { // 线程内容 } } class NumberThread1 implements Runnable{ @Override public void run() { // 线程内容 } }
- 优点
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止