不知道有没有童鞋每每搞到多线程或者一提到JUC就望而却步;这一次让我们一起肝了它。

image-1672056838384

认识线程

进程 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。

image-1671295730267

各种语言对其实现稍有不同,可能会做自己的细分和合并。以java语言为例

Ready 和 Running 状态合并成了 Runnable;Waiting状态拆分成了 WAITING,TIMED_WAITING,BLOCKING

image-1671295768454

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();


线程的打断

  1. 正常的打断
    interrupt(); 打断设置标志位
    isInterrupt(); 查询标志位
    interrupted(); 静态方法;查询标志位,并重置标志位

  2. wait, sleep, join 可以被interrupt 打断

  3. 锁资源争抢不可以被interrupt打断

  4. 锁资源争抢的打断可以用ReetrantLock#lockInterruptlly() 打断

线程的停止

  1. 正常停止,运行结束,自然停止

  2. stop , suspend 等停止,方法过期不建议使用;打断腿,可能造成数据不一致

  3. volatile 关键字,判断标志位

  4. 利用interrupt 和 isinterrupt 比较优雅