多线程线程

Posted 烟锁迷城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程线程相关的知识,希望对你有一定的参考价值。

目录

1、线程的概念

2、线程的特点

3、线程的使用

3.1、thread类

3.2、runnable接口

3.3、Callable接口

4、线程的启动过程

5、线程的生命周期

6、线程的停止

6.1、中断标志

6.2、中断方法与复位

6.2.1、获取中断标志:isInterrupted()

6.2.2、线程中断:interrupt()

6.2.3、中断复位:interrupted()


1、线程的概念

开始学习多线程之前,要弄清出一个概念,什么是线程?

线程是轻量级的进程,每一个进程内都包含N个线程。

那么问题又来了,什么是进程?

进程是每一个系统操作的执行操作。

实际上,在进行系统操作时,无论是什么操作,都是需要CPU资源和磁盘IO的,磁盘IO相比其他操作,速度很慢,而CPU资源不可能等待IO,它非常宝贵,所以只能让CPU去执行其他的操作,这就是CPU的时间片切换

为了能让CPU有切换的进程,系统设计了一种多进程,多个进程同步进行,互相隔离互不影响,但进程太重了,为了轻量化,就产生了线程的概念

2、线程的特点

线程有两个特点,异步和并行

  • 异步:无需等待,分别执行程序
  • 并行:依赖CPU核心数

3、线程的使用

3.1、thread类

线程可以通过继承thread类,重写run方法,用start方法来进行调用。

public class ThreadDemo extends Thread
    @Override
    public void run() 
        try 
            Thread.sleep(2000);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        System.out.println("thread show");
    

    public static void main(String[] args) 
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
        System.out.println("main show");
    

3.2、runnable接口

实现Runnable接口后,重写run方法,用thread类的start方法进行调用

public class RunnableDemo implements Runnable 
    @Override
    public void run() 
        try 
            Thread.sleep(2000);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        System.out.println("thread show");
    
    public static void main(String[] args) 
        RunnableDemo runnableDemo = new RunnableDemo();
        new Thread(runnableDemo).start();
        System.out.println("main show");
    

3.3、Callable<T>接口

实现Callable<T>接口,重写call方法,可以实现有返回值的线程,但是要注意,这里future的get方法是阻塞的,因为需要返回值。

public class CallableDemo implements Callable<String> 

    @Override
    public String call() throws Exception 
        Thread.sleep(2000);
        return "callable show";
    

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        CallableDemo callableDemo = new CallableDemo();
        Future<String> future = executorService.submit(callableDemo);
        System.out.println(future.get());
        System.out.println("main show");
    

4、线程的启动过程

无论是继承Thread还是实现Runable接口,其重写的都是run方法,但执行的是start方法,因为run方法只是回调函数,真正启动线程的是start。

Thread的start方法是在JVM层面起作用,但是java和JVM都没有线程,真正要调度的是操作系统的线程

JVM的跨平台特性可以让任何操作系统在start方法执行之后创建线程开始线程,此时系统会根据CPU调度算法将线程分配给CPU

当CPU执行到某个线程Thread时,会调用其run方法,完成回调。

5、线程的生命周期

java线程有六种状态,分别是new(创建),runnable(运行),time_waiting(超时等待),waiting(等待),blocked(阻塞),terminated(死亡)。
系统线程的五种状态,分别是新生,就绪(ready),运行(running),等待(waiting),死亡。

  • 一个线程先被创建,然后进入运行(分为就绪,运行两种)状态 
  • 加锁(synchronized修饰)会进入阻塞状态
  • 使用不带等待时间的等待函数sleep(0),wait(),join(),LockSupport.park()会进入等待状态
  • 使用加入唤醒时间的等待函数sleep(time),wait(time),join(time),LockSupport.parkUntil(time)会进入超时等待状态
  • notify(),notifyall(),LockSupport.unpark()可以唤醒沉睡中的线程,使之进入运行(分为就绪,运行两种)状态
  •  run函数运行结束后,线程进入死亡状态。

6、线程的停止

6.1、中断标志

线程的停止可以使用stop方法,但是这是一个简单粗暴的命令,可能会导致强制停止,因此这里使用中断标志来停止。

相比于强制停止的stop方法,中断标志是一种更温和的终止线程的方式,它将当前线程是否终止的决定权交给线程。

中断标志看起来可能不够熟悉,不过InterruptedException异常想必很多人都遇到过,在执行sleep,wait等带有等待或超时等待的方法时,编译器会强制要求抛出此错误,而这个错误,就是中断标志错误。

  1. 将一个共享变量,即中断标志从false设置为true
  2. 将进入等待或超时等待状态下的线程唤醒,执行下一步的操作。

6.2、中断方法与复位

在中断标志中,有三个方法是很关键的,它们是interrupt(),isInterrupted()和interrupted()。

这三个方法是非常类似的,接下来我们来依次分析。

6.2.1、获取中断标志:isInterrupted()

isInterrupted()方法的使用方式为Thread.currentThread().isInterrupted(),获取当前线程的中断标志,默认是false,下面是示例代码

public class Demo implements Runnable 

    @Override
    public void run() 
        boolean interrupted = Thread.currentThread().isInterrupted();
        System.out.println(interrupted);
        System.out.println("processor stop");
    

    public static void main(String[] args) throws InterruptedException 
        Thread thread = new Thread(new Demo());
        System.out.println("main show");
        thread.start();
    

可以看到,在默认状态下,中断标志是false

6.2.2、线程中断:interrupt()

interrupt()方法的使用方式为Thread.currentThread().interrupt(),将调用此方法的线程的中断标志置为true

可以看到下面的例子中,执行interrupt()方法之后循环会被中断,处于等待之中的线程会被唤醒然后进入到catch代码块中,继续执行中断方法interrupt(),会将线程停止。

public class Demo implements Runnable 

    @Override
    public void run() 
        while (!Thread.currentThread().isInterrupted()) 
            try 
                System.out.println("thread show");
                Thread.sleep(2000);
             catch (InterruptedException e) 
                e.printStackTrace();
                Thread.currentThread().interrupt();
            
        
        System.out.println("processor stop");
    

    public static void main(String[] args) throws InterruptedException 
        Thread thread = new Thread(new Demo());
        System.out.println("main show");
        thread.start();
        thread.interrupt();
    

 如果把catch代码块中的interrupt()去掉,那么线程将继续执行完,不会中断,这就是所谓的中断复位

6.2.3、中断复位:interrupted()

interrupted()方法的使用方式为Thread.interrupted(),将调用此方法的线程的中断标志置为false,是主动的复位

可以看到,interrupted()不同于上两个方法,这只是Thread的静态方法,查看源码可知,此方法检测当前线程是否被中断, 如果是返回true , 否则返回false,如果是中断状态,则复位当前线程

public static boolean interrupted() 
    return currentThread().isInterrupted(true);

interrupted()方法是主动复位,还有被动复位的,那就是强制抛出错误InterruptedException,这就解释了之前为什么如果不再catch代码块中进行操作的话,线程会继续执行下去,因为InterruptedException自动将中断标志复位了。

中断标志复位的友好性在于,虽然中断的发起可以在线程之外,但具体的内部处理却可以完全由线程来进行控制。

以上是关于多线程线程的主要内容,如果未能解决你的问题,请参考以下文章

为啥 JavaScript 不支持多线程?

20200225 Java 多线程-廖雪峰

Java多线程-线程的优先级(priority)以及守护线程(daemon)

JAVA线程与多线程

多线程的并发一般不是程序员决定,而是由容器决定

okhttp(二)之线程池分析