二十五.线程
1.作用:
使代码可以同时执行多个事物
2.名词
进程:一个正在执行的程序
进程是程序的一次执行过程,或是正在运行的一个程序.是动态过程:有他自身的产生,存在,消亡
线程: 一个执行路径
线程是由进程创建的,是进程的一个实体,一个进程可以有多个线程
单线程:同一时刻,只允许执行一个线程
多线程:同一时刻,可以执行多个线程
主线程:一个线程创建时自带的一条线程,称为主线程
子线程:除主线程外都是子线程
守护线程(后台线程):
特点:如果一个进程的前台线程全部被销毁,此时不管是否有守护线程正在执行,此时进程都将被回收
前台线程:
特点:如果一个进程中有前台线程存活,此时系统将不会回收进程
3.组成:
CPU时间片
线程抢夺CPU执行权后可执行的时间
内存
一个线程一个栈内存,多个线程共享一个堆内存
4.线程的创建
继承Thread
方式一:
创建Thread的子类对象
步骤:
1.创建一个类
2.使其继承Thread
3.重写run方法
注意:当前线程启动后,将执行如方法中的代码
4.创建该类对象
优点:可以创建无数个该类对象
方式二:
使用匿名内部类的形式创建Thread的子类对象
步骤:
Thread 对象名 = new Thread(){
重写run方法
}
优点:便于书写
缺点:只能创建一个该线程对象
实现Runnable接口
方式一:普通方式创建Runnable的子类对象
步骤:
1.创建一个类
2.使其实现Runnable接口
3.重写run方法
4.创建该类对象
5.在创建线程对象时传入该类对象(线程任务对象)
方式二:
使用匿名内部类创建Runnable的子类对象
步骤:
1.Runnable 对象名 = new Runnable(){
重写run方法
};
2.创建线程对象时传入该类对象(线程任务对象)
5.线程使用
5.线程的启动
线程对象.start();
注意:
1.当线程启动后会开启新的执行路径,执行run方法中的代码
2.如果直接使用线程对象调用run方法,不会开启新的执行路径
6.线程的销毁
线程已经启动,无法控制,需要等待执行完run方法中的代码
7.获取当前线程对象
+Thread.currentThread();
8.线程名城
获取线程名称:
线程对象.getName();
修改线程名称
线程对象.setName(name);
注意:在启动前修改
9.线程优先级
作用:提高线程抢夺到CPU执行权的概率
语法:
线程对象.setPriority(int);
注意:
1.取值范围1-10;
2.必须在线程启动前
10.线程休眠
作用:让线程暂时停止运行
方法:
static void sleep(休眠时间);
注意:
1.单位毫秒
2.使用类调用,使当前线程休眠
3.线程休眠期间不会抢夺CPU执行权
11.线程礼让
作用:将获取道德cpu执行权让出,重新参与抢夺
方法:
static native void yield();
12.线程合并
作用:合并线程
方法:
public final void join();
13.守护线程
有名:后台线程
特点:当一个进程中有前台线程存活,此时该进程就不会被系统回收
如果一个进程中只剩余后台线程,此时不管后台线程中的代码是否运行完毕,系统都将回收该进程
方法:
public final void setDaemon(boolean on);
true:表示守护线程,默认为false,表示前台线程
注意:
1.创建线程对象与主线程默认为前台线程
2.必须在线程启动前
6.线程的生命周期
线程可以处于以下状态之一
1. NEW: 尚未启动的线程处于此状态
2. RUNNABLE:在java虚拟机中执行的线程处于此状态;内部会分为ready和running两部分,执行哪一个由调度器决定
3. BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
4. WAITING :正在等待另一个线程执行特定动作的线程处于此状态
5. TIMED_WAITING :正在等待另一个线程执行动作达到指定等待时间的线程处于此状态(超时等待)
6. TERMINATED:一退出的线程出于此状态
7.Thread和Runnable的区别
1. 从java设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以见到Tread类本身就实现了Runnable接口
2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
8.线程安全问题
原因:
多个线程操作同一数据,会导致线程安全问题
解决思路
保证同时只能有一个线程操作该数据
方案
方案一:同步代码块
语法:
synchronized(锁对象){
要同步的代码
}
注意:
1.所有对象都可以作为锁对象
2.多个线程的锁对象要是同一个对象
方案二:同步方法
语法:
访问权限修饰符 synchronized 返回值类型 方法名(形参列表){
}
注意:
1.同步方法法人锁对象是this
2.多个线程的锁对象要是同一个对象
方案三:同步静态方法
语法:
访问权限修饰符 synchronized static 返回值类型 方法名(形参列表){
}
注意:
1.同步静态方法的锁对象是该类的对象
类对象:当JVM加载类时.会产生一个对象,该对象就是该类的类对象,一个类只有一个类对象
2.多个线程的锁对象要是同一个对象
9.死锁
原因:
多个线程互相持有对方所需的锁资源
举例
Object A = new Object();
Object B = new Object();
线程1:
xxx
synchronized (A){
xxx
synchronized (B){
xxx
}
}
线程2:
xxx
synchronized (B){
xxx
synchronized (A){
xxx
}
}
避免思路
不要在同步中使用
10.线程间通讯
注意
1.线程间通讯的方法是由Object提供的
2.只能在同步中使用
3.只能使用所在的同步的锁对象调用
方法
唤醒线程
notify():随机唤醒一个
notifyAll():唤醒所有
注意:只能唤醒以同一个对象调用wait方法的线程
线程休眠:
wait() 无限休眠
wait(int ms) 有限期休眠(休眠ms毫秒)
wait(int ms,int ns)有限期休眠
注意:
ms为毫秒,ns为纳秒
11.wait和sleep的区别
1.wait在休眠期会释放所持有的锁资源,sleep不会
2.wait必须在同步使用,sleep没有限制
3.wait必须使用所在的同步锁对象调用,sleep使用Thread类或者Thread类的对象调用
4.wait由Object提供,sleep由Thread类提供
13.线程池
作用:
优化线程
原因:
一个线程大约占1MB的运行内存
大量创建线程时可能会导致内存溢出
大量的创建线程也导致需要对象线程频繁的创建与回收
思路
1.使用空闲的线程执行新的任务(线程复用)
2.使用一个容器来管理线程的创建,回收,复用等
注意:java中提出线程池来完成以上操作
线程池的体系结构
Executor(接口)
提供的方法:
void execute(Runnable command);
作用:执行线程任务,子接口提供了submit优于该方法
子类或子接口
ExecutorService(接口)
提供的方法:
void shutdown();
作用:关闭线程池
boolean isShutdown();
作用:判断线程池是否关闭
Future<T> submit(线程任务)
作用:提交线程任务
子类或子接口:
ScheduledExecutorService(接口)
ThreadPoolExecutor(类)
构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maxmumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:h核心线程数,线程池中最少有几个线程
maximumPoolSize:最大线程数,线程中最多可以容纳的线程数
keepAliveTime:销毁时间,当线程执行完任务后,多久销毁
until:时间单位
workQueue:存储执行的线程任务的集合(队列形式)
threadFactory:创建线程
handler:优化线程,使其线程复用的算法
Executors
作用:创建线程池
原因:应为ThreadPoolExecutor过于麻烦,所以JDK提供该类帮助我们创建线程池提供的方法
1.固定线程池
特点:线程池中的线程数量恒定,当线程任务小于线程数量时,随机在线程池中挑选线程执行任务,当线程任务大于线程数量,会先执行前面的任务,后等前面任务执行完毕后,使用执行完毕的线程,执行剩余任务.
static ExecutorService newFixedThreadPool(int nThreads)
nThreads:线程池中线程的数量
2.可变线程池
特点:线程池中的线程数量可变
static ExecutorService newCachedThreadPool()
3.单例线程池
特点:一个线程池只有一个线程
static ExecutorService newSingleThreadExecutor()
4.抢占线程池
static ExecutorService newWorkStealingPool()
5.调度线程池
特点:该线程池执行任务可以延迟,也可以延迟重复执行
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
corePoolSize:线程池中线程的数量
6.单例调度线程池
特点:调度线程池中只有一个线程
static ScheduledExecutorService newSingleThreadScheduledExecutor();
线程池的使用
步骤:
1.创建线程池
2.提交任务
3.关闭线程池
调度线程池:ScheduledExecutorService
提供的方法:
public ScheduledFuture<?> schedule(线程任务,long delay,TimeUnit unit)
作用:延迟执行
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command
long initialDelay
long period
TimeUnit unit);
作用:延迟重复执行
1参,执行的任务
2参,延迟时间
3参,间隔时间 = 本次任务开始时间 - 上次任务开始时间
注意:如果上次任务执行时间>间隔时间,那么当上次任务执行完毕后,本次任务直接开始
4参,时间单位
public ScheduledFuture<?> scheduleWithFixedDaley(Runnable command,
long initialDelay
long delay
TimeUnit unit);
作用:延迟重复执行
1参,执行任务
2参,延迟时间
3参,间隔时间= 本次任务开始时间-上次任务结束时间
4时间单位
14.Callable
作用:有返回值的线程任务对象
注意:无法在创建Thread对象时传入,必须配合线程池使用
15.Lock
作用:简化同步
同步:
同步代码块
synchronize(锁对象){
//关锁
代码
//开锁
}
同步方法
访问权限修饰符 synchronize 返回值类型 方法名(形参列表){
//关锁
方法
//开锁
}
同步静态方法
访问权限修饰符 synchronize static 返回值类型 方法名(形参列表){
//关锁
方法
//开锁
}
lock体系
Lock
提供的方法:
void lock():关锁
void unLock():开锁
Condition newCondition():获取锁对象
void await():无限期休眠
void signal():随机唤醒一个
void signalAll():唤醒所有
子类:
ReentrantLock
ReadWriteLock
提供的方法:
Lock readLock():获取读锁
Lock writeLock():获取写锁
注意:
读-写 互斥
读-读 不互斥
写-写 互斥
子类
ReentrantReadWriteLock