Java多线程笔记
1、多线程基本概念
- 进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源 - 线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
2、并行和并发的区别
- 并行:多个cpu同时执行多个任务,发生在某一刻
- 并发:一个cpu同时执行多个任务,发生在某个时间段
3、创建线程的方式
3.1 方式一:继承Thread类
- 1、继承Thread类,重写父类的run()方法
- 2、调用线程对象的start()方法
3.2 方式二:实现Runnable接口
- 1、实现Runnable接口,重写接口的run()方法
- 2、通过Thread类的构造器创建多线程
- 3、将Runnable的实现类作为参数传递给Thread类的构造器,创建Thread类的对象
- 4、调用Thread类的start()方法开启线程
3.3 方式三:实现Callable接口
1、实现Callable接口,重写call()方法
2、将Callable接口的实现类作为参数,传递给FutureTask类的构造器,创建FutureTask类的对象
3、将FutureTask类作为参数传递给Thread类的构造器,创建Thread类的对象
4、调用Thread类的start()方法开启线程
5、较Runnable相比,具有返回值、可抛出异常
6、获取返回值,需借助FutureTask类的get()方法,在开启start()方法之后调用
4、继承Thread和实现Runnable的区别
继承Thread:线程代码存放在Thread子类run()方法中
实现Runnable:线程代码存放在接口的子类的run()方法中
实现Runnable方式,避免了单继承的局限性
多个线程可以共享同一个接口实现类的对象(也叫共享数据),适合多个线程来处理同一份资源
5、线程的生命周期
线程一共有五种状态,线程在各种状态之间转换。
6、线程同步
- 安全问题出现的条件
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据
- 如何解决多线程安全问题呢?
- 基本思想:让程序没有安全问题的环境
- 怎么实现呢?
- 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
- Java提供了同步代码块的方式来解决
- 同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁 - 同步的好处和弊端
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
- Lock锁
-
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
-
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
-
- synchronized与Lock的区别
- synchronized:
1、隐式锁出了作用域自动释放
2、具有同步代码块锁和方法锁,2种锁方式 - Lock:
1、显示锁(需手动开启和关闭锁)
2、只有代码块锁,没有方法锁
3、JVM将花费更少时间调度线程,性能会更好些
- synchronized:
8、线程的死锁
8.1 死锁原因
1、不同的线程分别占用对方需要的同步资源不放弃,都在等待对方释放自己需要的同步资源,就形成了死锁
2、出现死锁后,并不会抛出异常和提示信息,所有线程处于阻塞状态,无法继续执行操作
8.2 解决方案
1、尽量减少同步资源的定义
2、尽量避免使用嵌套同步
9、生产者消费者
生产者和消费者模式概述
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
所谓生产者消费者问题,实际上主要是包含了两类线程:一类是生产者线程用于生产数据,一类是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
10、线程的通信
线程与线程之间难免会相互协作去完成一件较复杂的功能,典型的例子生产者-消费者
- wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
- notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个
- notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
1、wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中
2、wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现IllegalMonitorStateException异常
3、wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中