您的位置 首页 java

并发编程系列——1线程基础

学习目标

  1. 线程 是什么?线程与进程的区别与联系
  2. 并行与并发的区别和联系
  3. 线程的创建方式有哪几种,以及它们各自的特点
  4. 线程的 生命周期 有哪些
  5. 线程怎么启动停止的

第1章 线程简介

1.1 何为线程

1、线程的定义:程序中的一个顺序控制流程,也是 CPU 中的最小调度单位。

从定义来看,可能很多同学觉得有点晦涩难懂,OK,我这里举个例子就明白了,其实在我们最开始学习 Java 的时候就写过 Hello World 程序对吧,在一个main方法里面定义一条打印语句,然后执行main方法就能完成打印,那同学们有没有想过,到底是由什么玩意儿来执行的main方法呢?

其实就是有我们的线程来执行的,在我们java中,任何代码的执行都是由线程来完成的,那线程追溯到计算机底层其实又是由CPU来完成的。

OK,到这里就明确了,计算机执行任何程序都是由CPU来完成的,而CPU真正执行的是一个个的线程,然后由线程去完成相应代码的执行。

好,从计算机中反应到现实中,每个线程就好比每个不同的人,每个人都自己的独立完成工作的能力,线程也可以这么理解,以后在我的课堂中,人就是线程,线程就是人。

2、进程的定义:运行中的应用程序。(没有运行的应用程序只是一堆静态代码,这里可以通过查看计算机进程验证)

3、进程和线程的关系:一个进程由多个线程构成。

1.2 线程的关系户

1、线程与CPU核心数的关系。如果线程数小于或者等于CPU核心数的时候,则CPU能同时运行,如果,大于的时候,CPU只能通过上下文切换的方式轮转交替执行线程。

2、守护线程和本地线程

  • Java中的线程分为两种:守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolean);true则把该线程设置为守护线程,反之则为用户线程。 Thread .setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
  • 两者的区别: 唯一的区别是判断虚拟机( JVM )何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。也可以理解为守护线程是JVM自动创建的线程(但不一定),用户线程是程序创建的线程;比如JVM的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是 Java虚拟机 上仅剩的线程时,Java虚拟机会自动离开。

1.3 线程的作用

  • 说的官方一点就是线程的使用能够提高CPU的效率,说的直白一点就是人多了,干一件事就快了,干多件事也快了,反正就是人多干活快。
  • 举个例子,如果现在你要往数据库里面插入1千万条数据(暂时先不考虑数据库的性能问题)然后你一个线程一条条插可能要10分钟,但是如果你是10个线程同时执行,可能就只要少于1分钟就能搞定。
  • 虽然线程很好用,但是并不是线程越多越好,就看人一样的,虽然人多力量大,但是人太多了,地球妈妈也扛不住啊,所以线程太多了,CPU扛不住,为什么扛不住呢?看上面讲的核心数。

1.4 线程的使用

OK,知道线程是啥了,也知道他很牛*了,接下来我们都开始用它了。怎么用呢?这也是一道简单的面试题,当面试官问你 jdk 提供几种线程创建方式的时候,其实有三种回答。

1、通过继承Thread类

这里可以带着同学们进到底层start0()方法,然后解释一下start方法是怎么调用的。

  • 自定义Girl类,然后继承Thread类
  • 重写run方法(这个方法里面的逻辑是具体要干的活,比如这个女孩要生小孩)
  • 创建Girl类的对象,Girl girl= new Girl();
  • 调用对象的start方法,girl.start();

2、通过实现Runnable接口

Runnable实际上不能理解成线程,应该理解成任务更加准确,既然是任务,它就不能自己去执行,必须得通过线程(人)去执行

  • 自定义MyTask类,然后实现Runnable接口
  • 重写run方法(这里也是具体要干的活)
  • 创建MyTask类的对象,MyTask task = new MyTask(); 注意,我们现在创建的只是一个任务,不能指望任务自己去完成自己,所以还得通过下一步的操作
  • 创建一个基本的线程,Thread ren = new Thread(task);
  • 调用线程的start方法,ren.start()

3、通过实现Callable接口

  • 自定义MyCallable类,然后实现Callable接口
  • 重写call方法(这里也是具体要干的活)
  • 创建MyCallable类的对象,MyCallable calla= new MyCallable(); 这里也是一个任务,也只能通过线程去执行,但是它只能通过 线程池 去做
  • 创建一个线程池,ExecutorService executorService = Executors.newFixedThreadPool(3);executorService.submit(calla);

它与Runnable的区别在于有无返回值,和能否抛异常

4、 线程池

这个在后面的文章中介绍

高手回答:jdk只有一种创建线程的方式,因为不论是上面四种中的哪一种方式,其实最终都是通过Thread去创建

顶手回答:jdk没有创建线程的能力,因为最终走到底层会发现,其实JVM是调用了底层操作系统的方法,由操作系统去真正创建线程了

1.5 线程的生命周期

刚刚已经给大家演示了怎么创建一个线程,然后怎么使用它,OK,那现在我们来总结一下,线程从出生到死亡,它走过了哪些阶段:

1、new,初始化状态;(创建类的这个过程还是能算是前期准备哈,线程这个人还没出生,这个时候不能算,类的创建阶段还只能算备孕阶段,只有通过new关键字创建了对象,才能算开始)

2、runnable,运行状态

  • start,就绪状态,这个状态还没有真正执行,只是告诉CPU,我准备好了,你可以来让我干活了。
  • running,运行中状态,拿到CPU给的工作牌,开始干活

3、timed_waiting,限期等待

sleep(10)/wait(10)/parkNanos(10)

4、waiting,无限期等待状态

wait()/park()

5、blocked,阻塞状态

在用锁的情况下( synchronized ),没有获取到锁的线程进入blocked状态。限期和无限期等待也可以称为阻塞状态。

6、terminated:终止状态,执行完run方法之后,自动被回收。

1.6 线程启动和停止

1、start方法启动

2、终止:

  • run方法执行完了之后自动终止
  • Thread.stop(不建议使用,对程序不友好,如果线程任务正在执行的话,调用stop会强制是的线程中断)
  • Thread.interrupt(),这个是友好的中断。

interrupt ()作用:更改线程的中断标记,唤醒处于阻塞状态下的线程。

会抛出 Interrupted Exception,线程自己决定要不要结束,异常会触发复位

interrupted():获取线程的中断标记,并且执行复位操作

isInterrupted():获取线程的中断标记,但是不会进行复位操作

代码举例

 public class InterruptDemo02 implements Runnable{
    @Override
    public  void  run() {
        while(!Thread.currentThread().isInterrupted()){ //false
            try {
                TimeUnit.SECONDS.sleep(20);
                System.out.println("1231");
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().isInterrupted());
                //可以不做处理,
                //继续中断 ->
                Thread.currentThread().interrupt(); //再次中断
                //抛出异常。。
            }
           // Thread.currentThread().interrupted();  //再次复位
            System.out.println(Thread.currentThread().isInterrupted());
        }
        System.out.println("processor End");
    }
    public  static  void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new InterruptDemo02());
        t1.start();
        TimeUnit.SECONDS.sleep(5);
      //  Thread.sleep(1000);
        t1.interrupt(); //有作用 true
    }
}  

下文预告

  1. synchronized使用方式
  2. synchronized锁状态及升级
  3. volatile 的作用

文章来源:智云一二三科技

文章标题:并发编程系列——1线程基础

文章地址:https://www.zhihuclub.com/189034.shtml

关于作者: 智云科技

热门文章

网站地图