Java多线程学习

举报
cqy_self 发表于 2021/10/27 11:46:32 2021/10/27
【摘要】 多线程的概念

1. 多线程

1.1概念

多线程的概念

程序、进程与线程:

程序:一段静态的代码,它是应用软件执行的蓝本。

进程:程序的一次动态执行过程(动态概念),它对应了从代码加载、执行到执行完毕的完整过程。一个程序可以被多次加载到系统的不同区域分别执行,形成不同的进程。

线程:比进程更小的执行单位(动态概念)。一个进程在执行过程中可产生多个线程,形成多条执行线索。每个进程都有一段专用的内存区,并以PCB(Process Control Block)作为它存在的标志;而一个进程中的所有线程可以共享该进程的同一个地址空间和操作系统资源,并利用这些共享单元实现数据交换、实时通信与必要的同步操作。因此多线程占用系统资源少、线程间通信快。

1.2线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。下图显示的是线程的生命周期:


1.2.1 新建状态:

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

1.2.2 就绪状态

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

1.2.3 运行状态

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

1.2.4 阻塞状态

如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

1.2.5 等待阻塞

运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

1.2.6 同步阻塞

线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)

1.2.7 其他阻塞

通过调用线程的 sleep() join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

1.2.8 死亡状态

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

1.3线程优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度序。

Java 线程的优先级是一个整数,其取值范围是 1 Thread.MIN_PRIORITY - 10 Thread.MAX_PRIORITY )。默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

 

2. 多线程实现

2.1 继承Thread

通过定义java.lang包中的Thread类的子类并在子类中重写run()方法。由于java不能多重继承,此方法简单但不灵活。Thread类的构造函数及主要方法如下:

public Thread():产生一个名字自动生成的线程,名字形式为Thread_1Thread_2、、、;

public Thread(Runnable target):生成一个指定目标对象的线程;

public Thread(String name):生成一个指定名字的线程;

public Thread(ThreadGroup group,Runnable target):生成一个指定线程组和目标对象的线程;

public Thread(ThreadGroup group,String name):生成一个指定线程组和名字的线程。

通过继承Thread类实现多线程示例:ThreadTest.java程序中subThread类继承了Thread类,先定义了一个构造函数调用父类的构造函数给该线程置名,然后重写了run()方法,使线程运行时每输出一个循环变量后休眠一段随机时间,让另一个线程运行,一个线程的run()方法结束前输出该线程的结束信息。

2.2 实现Runnable接口

Runnable接口只有一个run()方法,要实现此接口就必须定义run()方法的具体内容,方法体内可定义用户要做的操作。然后以这个实现了Runnable接口的类为参数创建Thread类的对象,也就是用Runnable接口的目标对象初始化Thread类的对象,如此就可把用户实现的run()方法继承过来。

创建一个线程,最简单的方法是创建一个实现Runnable接口的类。

为了实现Runnable,一个类只需要执行一个方法调用run(),声明如下:

Public void run()

在创建一个实现Runnable接口的类之后,你可以在类中实例化一个线程对象。

Thread(Runnable threadObString threadName)

threadOb 是一个实现Runnable接口的类的实例,并且threadName指定新线程的名字。

新线程创建之后,调用start()方法才会运行。

void start()

2.3 通过CallableFuture创建线程

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并有返回值。

创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

使用FutureTask对象作为Thread对象的target创建并启动新线程。

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

 

3. 多线程的基本控制

在控制线程从一种状态转入另一种状态时,必须调用正确的方法,否则将产生异常。除了注意状态转换时需调用的方法外,为使多个线程之间能协调工作,必须控制线程的互斥、同步及死锁问题。

3.1互斥

一组并发进程中一个或多个程序段,因共享某一公用资源而导致它们必须以一个不允许交叉执行的单位执行。即不允许两个以上共享该公用资源的并发进程同时进入临界区。

3.2同步

一组并发进程因直接制约而互相发送消息,进行相互合作,互相等待,使得各进程按一定的执行速度执行的过程,称为进程间同步。

3.3死锁

在多个进程并行执行时,当某进程提出资源申请后,使得若干进程在无外力作用下,永远得不到所需要的资源,不能再继续运行的情况。

如生产者与消费者问题:生产的产品存入仓库,消费时从仓库取出产品。当仓库满时再生产将无处可放,而当仓库空时再消费将取不到产品。采用同步策略可协调生产与消费.

 

4. 线程通信

4.1 线程通信机制

线程间同步可以归纳为一个线程间通信的一个子集,线程通信是指两个线程之间可以交换一些实时的数据信息。

4.2 实现线程间的通信方法(wait/notifyLock/Condition

线程通信的waitnotify机制

- 等待/通知机制

- wait就是线程在获取对象锁后,主动释放对象锁,同时本线程休眠。知道线程调用notify唤醒该线程,才能继续获取对象锁,继续执行

- 都是Object类的方法

线程通信的LockCondition机制

-Lock用于控制多线程对需要竞争的共享资源的顺序访问,保证状态连续性

-Conditionjava提供实现等待/通知的类,由Lock对象所创建

-Condition中的wait方法相当于Objectwait方法,signal方法相当于Objectnotify方法

5.JUC并发包

5.1 线程的ThreadLocal本地缓存对象

ThreadLocal线程范围内的共享变量:线程范围内的共享变量,每个线程只能访问自己的数据,不能访问别的线程数据。

 

每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。

 

5.2 线程的volatile关键字

volatile关键字可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。

 

volatile的作用:使变量在多个线程间可见,但是无法保证原子性。需要注意的是一般volatile用于只针对多个线程可见的变量操作,并不能代替synchronized的同步功能。

 

5.3 线程池的作用和应用

线程池的作用:

--1、降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

--2、提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。

--3、提高线程的可管理行:线程是稀缺资源,如果无限制的创建,不仅会消耗系统消耗,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

 

5.4 线程的同步工具类CountDownLatch

CountDownLatch同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待:

--1CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用countDown()方法,计数器减1,计数器大于0时,await()方法会阻塞程序继续执行。

--2、由于调用了countDown()方法,所以在当前计数到达零之前,await()方法会一直受阻塞。之后,会释放所有等待的线程,await()的所有后续调用都将立即返回。这种现象只出现一次,计数无法被重置。一个线程或者多个,等待另外N个线程完成某个事情之后才能执行。

 

5.5 线程的同步工具类CyclicBarrier

CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公众屏障点(common barrier point)。因为该barrier在释放等待线程后可以重用,所以称它为循环的battier

5.6 线程的同步工具类Semaphore

Semaphore是一个计数信号量,它的本质是一个共享锁,是基于AQS实现的,通过state变量来实现共享。通过调用acquire方法,对state值减去一,当调用release的时候,对state值加一。当state变量小于0的时候,在AQS队列中阻塞等待。

5.7 线程的交换类Exchanger

Exchanger(交换者)是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换。

它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。

这两个线程通过exchanger()交换数据。

如果第一个线程先执行exchanger()方法,它会一直等待第二个线程也执行exchanger(),当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

 

5.8 线程的Fork/Join机制

Fork/Join框架是Java7提供一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

分治法:把一个规模大的问题划分为规模较小的子问题,然后分而治之,最后合并子问题的解得到原问题的解。

 

6.常用线程池

6.1 优势

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队到,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。

他的主要特点为:线程复用:控制最大并发数:;管理线程。

第一:降低资源消耗。通过重复利用己创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

 

6.2 四种线程池

6.2.1 newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

6.2.2 newFixedThreadPool

 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

6.2.3 newScheduledThreadPool

创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。

6.2.4 newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 

6.3 核心类

int corePoolSize, 核心线程大小

int maximumPoolSize,最大线程大小

long keepAliveTime, 超过corePoolSize的线程多久不活动被销毁时间

TimeUnit unit,时间单位

BlockingQueue<Runnable> workQueue 任务队列

ThreadFactory threadFactory 线程池工厂

RejectedExecutionHandler handler 拒绝策略

 

6.4线程池任务执行流程

当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。

当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行

workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务

当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理

当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,释放空闲线程

当设置allowCoreThreadTimeOut(true)时,该参数默认false,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。