不知道有没有童鞋每每搞到多线程或者一提到JUC就望而却步;这一次让我们一起肝了它。
认识线程
进程 vs 线程
说到多线程,我们首先的认识什么是线程,什么是进程;得搞清楚这些概念。
进程是操作系统资源分配的基本单位;这个概念很好理解就是我们的应用程序,例如:QQ,微信等。
线程是CPU调度执行的最小单位;一个进程可以有一个或者多个线程。
串行 vs 并行 vs 并发
串行就是一个一个顺序执行;
并行是指“并排行走”或“同时实行或实施” (一起上);,无论从微观还是宏观,程序都是一起执行的
并发是指在同一个时间段内,两个或多个线程执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)
同步异步、阻塞非阻塞
同步异步,阻塞非阻塞的概念;很容易搞混,每次简单一聊能说个123,一深入探讨就说不出个所以然。
同步异步:调用者发起调用后,被调用者会不会主动通知;
阻塞非阻塞:调用者发起调用后,调用者用不用一直等待处理结果;
同步异步关注的是被调用者的反馈的方式;阻塞与非阻塞关注的是调用者后续的行为。
它们有如下组合:
同步阻塞:瓦特烧开水,水烧开不会主动通知;烧水开始执行后,瓦特需要一直等待水烧开。
同步非阻塞:瓦特烧开水,水烧开不会主动通知;烧水开始执行后,瓦特不需要一直等待水烧开,可以去执行其他功能,但是需要时不时的查看水开了没
异步阻塞:瓦特烧开水,水烧开会主动通知;烧水开始执行后,瓦特需要一直等待水烧开。
异步非阻塞:瓦特烧开水,水烧开会主动通知;烧水开始执行后,瓦特不需要一直等待水烧开,可以去执行其他功能。
我们从效率上来看异步非阻塞的效率最高。
多线程
多线程就是单个进程中多个线程一起工作。
多线程的目的是为了提高CPU的利用率。有人可能会问单核CPU还有必要用多线程吗?其实是有必要的,我们可以在等待磁盘IO或者网络IO时,让CPU去做其他的事情,提高CPU的利用率。
当然线程的数量也不是越多越好;CPU执行线程上下文切换是有成本的,那你可能问该如何设置线程数呢?
理论算法:
Nthreads = Ncpu * Ucpu * W /(W + C) = Ncpu * Ucpu * ( 1 + W/C)
Nthreads : 线程数
Ncpu: CPU核数
Ucpu:期望CPU利用率,应当是介于0~1之间的数值
W:等待时间
C:计算时间
W/C:等待时间和计算时间比率
实际算法:
评估应用程序是IO密集型还是CPU密集型结合计算机硬件情况;先设定预估一个值,然后在进行压测,对预估值进行调优。
线程状态
现代计算机的设计,线程一般分为5个状态:New,Ready,Running,Waiting,Terminated。
各种语言对其实现稍有不同,可能会做自己的细分和合并。以java语言为例
Ready 和 Running 状态合并成了 Runnable;Waiting状态拆分成了 WAITING,TIMED_WAITING,BLOCKING
NEW:Thread对象被创建出来了,但是还没有执行start方法。
RUNNABLE:Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程
BLOCKED:synchronized没有拿到同步锁,被阻塞的情况
WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒
TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒
TERMINATED:run方法执行完毕,线程生命周期到头了
在Java代码中验证一下效果
NEW
public static void testNewState() {
Thread t1 = new Thread();
System.out.println(t1.getState());
}
RUNNABLE
public static void testRunnableState() {
Thread t1 = new Thread(() -> {
while (true) {
}
});
t1.start();
System.out.println(t1.getState());
}
WAITING
public static void testWaitingState() throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
synchronized (o) {
try {
o.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
//等待以下确保线程启动成功
Thread.sleep(500L);
System.out.println(t1.getState());
}
TIMED_WITING
public static void testTimedWaitingState() throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
synchronized (o) {
try {
o.wait(5000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
Thread.sleep(500L);
System.out.println(t1.getState());
}
BLOCKED
public static void testBlockedState() throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
synchronized (o) {
}
});
synchronized (o) {
t1.start();
Thread.sleep(500L);
System.out.println(t1.getState());
}
}
TERMINATED
public static void testTerminatedState() throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(() -> {
});
t1.start();
Thread.sleep(500L);
System.out.println(t1.getState());
}
创建线程的方法
继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread类子类实现方式");
}
}
}
实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run(){
System.out.println("Runnable实现方式");
}
}
匿名内部类以及lambda表达式
class Test {
public static void main(String[] args){
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
System.out.println("匿名内部类实现方式");
}
});
t1.start();
Thread t2 = new Thread(()->{
System.out.println("lambda 表达式实现方式");
});
t2.start();
}
}
Callable结合FutureTask实现方式
class MyCallable implements Callable{
public static void main(String[] args){
FutureTask task = new FutureTask(new MyCallable());
Thread t1 = new Thread(task);
t1.start();
//可以获取返回值
task.get();
}
@Override
public Object call(){
return new String("带返回值的方式");
}
}
线程池方式
class ExecutorServiceDemo {
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new MyRunnable());
executorService.shutdown();
}
}
线程的常用方法
//获取当前线程
Thread.currentThread();
//获取和设置线程名称
Thread t1 = new Thread();
t1.setName();
t1.getName();
//获取和设置线程优先级,范围1~10
Thread t2 = new Thread();
t2.setPriority();
t2.getPriority();
//线程的让步,让出CPU资源,其他线程不一定获取到
Thread.yeild();
//守护线程
Thread t3= new Thread();
t3.setDeamon(true);
//线程的join t5会在t4执行完之后执行,可以用来控制线程的顺序执行
Thread t4 = new Thread();
Thread t5 = new Thread(()->{
try{
t4.join();
}catch(InterruptException e){
throw new RuntimeException();
}
});
//线程的休眠
Thread.sleep(ms);
//线程的等待和唤醒,需要配置Synchronized
// wait() 会让出锁资源,进入等待池
// notify() 会随机唤醒一个在等待池的线程,进入锁池
// notifyAll() 会唤醒全部的线程,进入锁池
Object o =new Object();
Thread t6 = new Thread(()->{
synchronized(o){
for(int i=0;i<10;i++){
if(i==5){
o.wait();
}
}
}
});
Thread t7 = new Thread(()->{
synchronized(o){
for(int i=0;i<10;i++){
if(i==5){
o.wait();
}
}
}
});
t6.start();
Thread.sleep(100L);
t7.start();
Thread.sleep(3000L);
o.notifyAll();
线程的打断
-
正常的打断
interrupt(); 打断设置标志位
isInterrupt(); 查询标志位
interrupted(); 静态方法;查询标志位,并重置标志位 -
wait, sleep, join 可以被interrupt 打断
-
锁资源争抢不可以被interrupt打断
-
锁资源争抢的打断可以用ReetrantLock#lockInterruptlly() 打断
线程的停止
-
正常停止,运行结束,自然停止
-
stop , suspend 等停止,方法过期不建议使用;打断腿,可能造成数据不一致
-
volatile 关键字,判断标志位
-
利用interrupt 和 isinterrupt 比较优雅
- 本文链接: https://www.sunce.wang/archives/这一次肝了多线程与高并发之基础篇
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!