基本概念
进程和线程
进程是程序运行资源分配的最小单位:进程是操作系统进行资源分配的最小单位,资源包括cpu,内存空间,磁盘IO等,同一进程中的多条线程共享该线程中的全部系统资源。
线程是cpu调度的最小单位,必须依赖于进程而存在。线程是进程的一个实体,是CPU调度和分配的基本单位,他是比进程更小的能独立运行的基本单位。
启动线程的三种方式:
1,extends Thread, 然后run
2, implements Runnable;然后交给Thread运行
3 ,implements Callable; 然后交给Thread运行
下面各举例说明一下:
//第一种方式: 声明线程类
public class MyThread extends Thread{
@Override
public void run() {
super.run();
//do somework
}
}
// 启动:
MyThead mThread = new MyThread();
mThread.start();
//方式二:
public class RunThread implements Runnable{
@Override
public void run() {
System.out.print("run run run....");
}
}
//调用和启动:
RunThread mRunThread = new RunThread();
new Thread(mRunThread).start();
//方式三:
public CallableThread implement Callabe<String>{
@Override
public String call() throws Exception {
return "CallThread";
}
}
//调用和启动:
CallableThread mCallThread = new CallableThread();
FutureTask<String> mTask = new FutureTask<>(mCallThread);
new Thread(mTask).start();
//接收返回参数
String param = mTask.get();
线程的终止:
1.自然终止:当线程run执行完了,或者抛出一个未处理的异常导致线程提前终止。
2 调用api方法时线程终止。其中有suspend() ,resume(),stop() .这些方法都是过时的,也是不建议使用的。以suspend()为例,调用后线程不会释放已经占有的资源,比如锁,而是占着资源进入睡眠状态,这样容易引发死锁问题。而stop()方法,结束一个线程时,不能保证所占资源正常释放, 通常是没有给线程完成任务释放资源的机会,程序会工作在不确定的状态下。因此不建议使用这些过时的方法。
安全的中止则是 其他线程调用某个线程A的interrupt()方法,对其进行中断操作,好比是其他线程对A线程打个招呼,“嘿 伙计 你要暂停了”。但是A不一定会立即中止,A完全可以不用理会这种中断请求,因为Java 线程是协作式的 不是抢占式的。 线程通过检查自身中断标示位是否被置为true来进行响应,通过方法 isInterrupted(), 来判断是否被中断,。
也可以通过调用静态方法Thread.interrupted(),来判断当前线程是否被中止,不过此方法会将自身中断标示位置为false。
不建议使用标示位来中断线程,因为run()方法里如果有阻塞操作时,无法很快检测到取消标识。要等阻塞执行完成后才会检测阻塞标识。这种情况使用中断会更好,1.一般的阻塞方法如sleep支持中断的检查,2,检查中断位的状态和检查取消标志位没什么区别,使用中断还可以避免声明取消标示位,减少资源的消耗。
处于死锁状态的线程无法被中断
对其他方法的理解
run()和 start()
start()才会开启运行一个线程,调用start方法后使线程进入就绪状态,等待分派资源cpu等资源,进入运行时才会调用实现了的 run方法。start不能被重复调用。
run只是调用类内部的一个成员方法。可以重复执行,可以单独调用。
yield()方法:
使当前线程让出cpu占有权,单让出时间是不可确定的。也不会释放锁资源,所有执行yield方法的线程进入可执行状态后有可能马上又被执行。
join():
把指定的线程加入到当前线程,可以使两个交替的执行的线程合并成顺序执行。比如在线程B中调用了线程A的join()方法,那么要等A线程执行完后,才会执行B线程。
wait()
调用该方法后,线程会进入等待状态,只有等待其他线程通知或被中断才会返回。注意 调用wait会释放对象的锁
notify()
通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获得了对象的锁,没有获得对象锁的线程会重新进入等待状态。
notifyAll()
通知所有等待在该对象上的线程。
线程池
public ThreadPoolExecute(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedPolicyHandler handler
)
各参数意义:
corepoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时存活的时间,默认情况下该参数只在线程数大于corePoolSize时有效。
TimeUnit:keepAliveTime的时间单位
workQueue:必须是BlockingQueue阻塞队列,尽量使用有界队列。
threadFactory:创建线程的工厂,
handler : 拒绝策略 JDK实现了四种
1. AbortPolicy:直接抛出异常,也是默认的策略。
2.CallerRunsPolicy:用调用者所在的线程来执行
3.DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务
4.DiscardPolicy:直接丢弃任务。
当然也可以实现RejectPolicyHandler接口,自定义饱和策略。
任务特性:
Cpu密集型 机器的CPU核心数(顶多+1) (页缺失)Runtime.getRuntime().availableProcessor 可以获取核心数。
IO密集型 : cpu核心数 * 2(经验值)
混合型: 时间相差不大 拆分成两个线程池 ,相差大 忽略时间少的
悲观锁与乐观锁
synchronized 与 lock 都是悲观锁,(上下文切换 耗时2万个时钟周期 3-5ms)
乐观锁 CAS ,自旋(死循环)(0.6ns )
只有synchronized 会进入阻塞(被动 ) lock是等待(主动)
死锁三个条件
多个操作者M 真多多个资源N N<=M
争夺资源顺序不对
拿到资源不放手
活锁:解决方法是:sleep随机数 几毫秒。
CAS(compare and swap)
原子操作:synchronized 是原子操作
cas问题:
ABA问题:A---》B--》A 解决:版本戳
开销问题:线程一直执行 开销很大
只能保证一个共享变量的原子操作 解决:将多个变量打包成一个变量
Java主流锁的分类:
![](https://img.haomeiwen.com/i18418487/3b54480e20ad7d20.jpg)
网友评论