实现多线程方式一:继承Thread类
方法介绍
方法名说明
void run()在线程开启后,此方法将被调用执行void start()使此线程开始执行,Java虚拟机会调用run方法()
实现步骤
定义一个类MyThread继承Thread类在MyThread类中重写run()方法创建MyThread类的对象启动线程
两个小问题
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
实现多线程方式二:实现Runnable接口
Thread构造方法
方法名说明
Thread(Runnable target)分配一个新的Thread对象Thread(Runnable target, String name)分配一个新的Thread对象
实现步骤
定义一个类MyRunnable实现Runnable接口在MyRunnable类中重写run()方法创建MyRunnable类的对象创建Thread类的对象,把MyRunnable对象作为构造方法的参数启动线程
实现多线程方式三: 实现Callable接口
方法名说明
V call()计算结果,如果无法计算结果,则抛出一个异常FutureTask(Callable callable)创建一个 FutureTask,一旦运行就执行给定的 CallableV get()如有必要,等待计算完成,然后获取其结果
实现步骤
定义一个类MyCallable实现Callable接口在MyCallable类中重写call()方法创建MyCallable类的对象创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数创建Thread类的对象,把FutureTask对象作为构造方法的参数启动线程再调用get方法,就可以获取线程结束之后的结果。 三种实现方式的对比
实现Runnable、Callable接口
好处: 扩展性强,实现该接口的同时还可以继承其他的类缺点: 编程相对复杂,不能直接使用Thread类中的方法 继承Thread类
好处: 编程比较简单,可以直接使用Thread类中的方法缺点: 可以扩展性较差,不能再继承其他的类
设置和获取线程名称
方法名说明
void setName(String name)将此线程的名称更改为等于参数nameString getName()返回此线程的名称Thread currentThread()返回对当前正在执行的线程对象的引用
线程休眠
方法名说明
static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数
线程优先级
线程调度
两种调度方式
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
Java使用的是抢占式调度模型
随机性
假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
方法名说明
final int getPriority()返回此线程的优先级final void setPriority(int newPriority)更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
守护线程
方法名说明
void setDaemon(boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
死锁
概述
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行
什么情况下会产生死锁
资源有限同步嵌套
生产者和消费者模式
Object类的等待和唤醒方法
方法名说明
void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法void notify()唤醒正在等待对象监视器的单个线程void notifyAll()唤醒正在等待对象监视器的所有线程
阻塞队列基本使用
常见BlockingQueue:
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
BlockingQueue的核心方法:
put(anObject): 将参数放入队列,如果放不进去会阻塞
take(): 取出第一个数据,取不到会阻塞
Day14多线程
线程池
线程池
atic ExecutorService newCachedThreadPool() 创建一个默认的线程池static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池 使用Executors中所提供的静态方法来创建线程池
static ExecutorService newFixedThreadPool(int nThreads) : 创建一个指定最多线程数量的线程池
线程池参数详解
public ThreadPoolExecutor(
int corePoolSize
,
int maximumPoolSize
,
long keepAliveTime
,
TimeUnit unit
,
BlockingQueue
<Runnable> workQueue
,
ThreadFactory threadFactory
,
RejectedExecutionHandler handler
)
corePoolSize: 核心线程的最大值,不能小于
0
maximumPoolSize:最大线程数,不能小于等于
0,maximumPoolSize
>= corePoolSize
keepAliveTime: 空闲线程最大存活时间
,不能小于
0
unit: 时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂
,不能为null
handler: 任务的拒绝策略
,不能为null
线程池-非默认任务拒绝策略
RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类
ThreadPoolExecutor
.AbortPolicy
: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor
.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor
.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor
.CallerRunsPolicy
: 调用任务的
run()方法绕过线程池直接执行。
注:明确线程池对多可执行的任务数 = 队列容量 + 最大线程数
原子性
概述 : 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的值