多线程&JUC

文章目录

  • 多线程
    • 一.什么是多线程
    • 二.多线程的两个概念
    • 三.线程的实现方式
    • 四.常见的成员方法
    • 五.线程安全的问题
    • 六.死锁
    • 七.生产者和消费者

多线程

一.什么是多线程

进程:是程序的基本执行实体

理解:每一个运行的软件就是一个进程

线程:是操做系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位

理解:应用软件中互相独立,可以同时运行的功能

为什么要有多线程?

多线程&JUC_第1张图片

有了多线程就可以让程序同时做多件事情

可以提高程序的运行效率

多线程的应用场景

  • 软件中的耗时操做
  • 拷贝,迁移大文件
  • 加载大量的资源文件
  • 所有的聊天软件
  • 所有的后台进程

二.多线程的两个概念

1.并发

同一时刻,有多个指令在单个cpu上交替执行

2.并行

同一时刻,有多个指令在多个cpu上同时执行

并发和并行是有可能都在发生的

三.线程的实现方式

继承Thread类的方式

  • 定义一个类继承Thread
  • 重写run方法
  • 创建子类的对象,并启动线程

多线程&JUC_第2张图片

实现Runnable接口的方式

  • 定义一个类实现Runnable接口
  • 重写里面的run方法
  • 创建自己的类的对象
  • 创建一个Thread类对象,并开启线程

多线程&JUC_第3张图片

细节:getName()是Thread类中的方法,所以想要调用,则

需要获取到当前线程的对象,然后用这个对象去调用

利用Callable接口和Future接口的方式

**特点:**可以获取到多线程运行的结果

  • 创建一个类MyCallable实现Callable接口
  • 重写call(是有返回值的,表示多线程运行的结果)
  • 创建MyCallable的对象(表示多线程要执行的任务)
  • 创建FutureTask的对象(管理多线程运行的结果)
  • 创建Thread类对象,并启动(表示线程)

多线程&JUC_第4张图片

多线程三种实现方式对比

多线程&JUC_第5张图片

四.常见的成员方法

Thread类中常见的成员方法

多线程&JUC_第6张图片

在这里插入图片描述

细节:

1.如果我们没有给线程设置名字,线程也是有默认名字

格式:Thread-x(x是序号,从0开始)

2.如果我们要给线程设置名字,可以用set方法进行设置,也可以用构造方法进行设置

在这里插入图片描述

细节:

1.当JVM虚拟机启动之后,会自动的启动多条线程,其中有一条线程就叫做main线程

它的作用就是调用main方法,并执行里面的代码

2.哪条线程执行到sleep(),那么哪条线程就停留对应的时间

3.参数:时间,单位:毫秒

在这里插入图片描述

在计算机当中,线程的调度有两种

第一种,抢占式调度

所谓抢占式调度,就是多个线程在抢夺cpu的执行权

cpu在什么时候执行哪条线程是不确定的,执行多长时间也是不确定的,体现随机性

第二种方式,非抢占式调度

所有的线程轮流的执行

java中,采用的是第一种,抢占式调度

优先级越大,线程抢到cpu的概率就越大

优先级最低:1

最高:10

默认:5

在这里插入图片描述

多线程&JUC_第7张图片

细节:

当其他的非守护线程执行完毕之后,守护线程会陆续结束

守护线程的应用场景:

比如,聊天窗口,既可以聊天,也可以发文件

聊天看成一个线程,发文件看成另一个线程

此时,如果把聊天窗口关掉,那么发文件线程也就没有存在的必要了

所以,就可以把发文件线程设置为守护线程

在这里插入图片描述

尽可能的让出cpu

在这里插入图片描述

多线程&JUC_第8张图片

线程的生命周期

多线程&JUC_第9张图片

五.线程安全的问题

买票引发的安全问题

  • 相同的票出现了多次
  • 出现了超出范围的票

多线程&JUC_第10张图片

多线程&JUC_第11张图片

线程执行时,有随机性

同步代码块

把操做共享数据的代码锁起来

格式:

多线程&JUC_第12张图片

特点:

  • 锁默认打开,有一个线程进去了,锁自动关闭
  • 里面的代码全部执行完毕,线程出来,锁自动打开

锁对象是任意的对象,但是要保证锁对象是唯一

static Object obj = new Object();

同步代码块的两个细节

  • 锁对象一定是唯一的,一般用字节码文件对象:类名.class

  • synchronized一定要写在循环的里面

    说明:如果把synchronized写在了循环的外面,那么其他线程将没有机会去执行任务,任务都被线程一执行完了

同步方法

如果我们想把一个方法里面的所有代码都锁起来,那么就没有必要使用同步代码块了

可以把synchronized直接加到方法上,那么这个方法就是同步方法

格式:

修饰符 synchronized 返回值类型 方法名(方法参数){}

特点:

  • 同步方法是锁住方法里面的所有代码
  • 锁对象不能自己指定
    • 如果当前的方法是非静态的,它的锁对象是this,就是当前方法的调用者
    • 如果当前方法是静态的,它的锁对象是当前类的字节码文件对象

写同步方法的技巧:

先写同步代码块,然后把同步代码块抽取成一个方法

lock锁

JDK5以后提供了一个新的锁对象Lock

它的实现比synchronized有更广泛的锁定操做

它提供了两个方法:

void lock():获得锁

void unlock():释放锁

这样就可以实现手动上锁和手动释放锁

注意:

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

ReentrantLock的构造方法:ReentrantLock(),创建一个ReentrantLock的实例

在这里插入图片描述

六.死锁

就是在程序中出现了锁的嵌套

多线程&JUC_第13张图片

七.生产者和消费者

又叫做等待唤醒机制

生产者消费者模式是一个十分经典的多线程协作的模式

我们知道多线程的执行具有随机性

那么,现在我们学习的生产者和消费者模式就要打破这一特性,使得多个线程轮流执行

生产者:生产数据

消费者:消费数据

多线程&JUC_第14张图片

理想情况

生产者先抢到cpu的执行权,进行生产数据,然后消费者再抢到cpu的执行权,进行消费数据

消费者等待

消费者先抢到cpu的执行权,这时发现没有数据,此时消费者处于等待状态,

当消费者处于等待状态时,生产者会抢到cpu的执行权,判断是否有数据,

如果没有,则生产数据,然后唤醒消费者,消费者进行消费数据

多线程&JUC_第15张图片

生产者等待

生产者先抢到cpu的执行权,判断是否有数据,有则进行等待

多线程&JUC_第16张图片

多线程&JUC_第17张图片

编写多线程代码的套路

  • 循环
  • 同步代码块
  • 判断共享数据是否到了末尾(到了末尾)
  • 判断共享数据是否到了末尾(没到末尾)

阻塞队列方式实现等待唤醒机制

阻塞队列好比是连接生产者和消费者之间的管道

生产者可以把生产的数据放到管道当中

消费者可以在管道中获取数据进而进行消费

我们可以规定管道中最多可以放多少数据

队列:数据在管道中,好比是排队一样,先进的数据,先出去

阻塞:

放数据时,放不进去,会等着,叫阻塞

取数据时,取不到,也会等着,也叫阻塞

阻塞队列的继承结构

阻塞队列一共实现了4个接口

iterable:表示阻塞队列可利用迭代器进行遍历或者增强for循环

collection:表示阻塞队列是单列集合

Queue:表示队列

BlockingQueue:表示阻塞队列

实现类:

ArrayBlockingQueue:底层是数组,有界,创建对象时必须指定长度

LinkedBlockingQueue:底层是链表,无界

不是真正的无界,最大为int的最大值

多线程的6种状态

多线程&JUC_第18张图片

在Java虚拟机当中,关于线程的状态,真正定义的只有6种状态,是没有定义运行状态的,

因为,当线程抢到cpu时,那么此时虚拟机就会把当前线程交给操做系统去管理了,所以就没有定义运行状态

你可能感兴趣的:(java入门笔记,java,多线程)