0
点赞
收藏
分享

微信扫一扫

1.24 并发(1)

开源分享 2022-02-18 阅读 82

1.线程介绍

线程是操作系统能够运行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位。
并行、异步

线程的意义:

在多核CPU中,利用多线程可以实现真正意义上的并行执行。

进程阻塞会引起不依赖该任务的进程也被阻塞。通过对不同任务创建不同的线程去处理,可以提升程序处理的实时性。

线程是轻量级的进程,线程的创建、销毁比进程更快。

2.线程的应用场景

1.使用多线程实现文件下载

2.后台任务:如向大量用户发送邮件

3.异步处理:记录日志

4.多步骤的任务处理,可根据步骤特征选用不同个数和特征的线程来协作处理,由一个主线程分割成多个线程完成。

本质是合理的利用多核芯CPU资源来实现线程的并行处理,来实现同一个进程内的多个任务的并行执行,同时基于线程本身的异步执行特征,提升任务处理的效率。

3.线程的创建

1.继承Thread类

重写Run()方法,实例化后使用start()方法运行。

继承的是类。

2.实现Runnable接口

类实现Runnable接口,重写run()方法,因为接口没有实现方法所以使用Thread的start()方法,传入类的参数,最后使用start()方法。

实现的是接口。 

3.实现Callable接口、Futuer

类实现Callable接口带有类型,自带返回值。运用线程池进行,实例化一个线程池,通过submit()方法接受类的信息,使用future拿到信息。最后用future.get方法得到。

存在一个返回值

4.线程池

PS: Thread的run()和start()方法的区别

Thread的start方法开启了新线程,并在线程中执行了run方法,而run则只是在当前线程中执行了其构造函数中传入的Runnable对象的run方法。

start()能够异步调用run()方法,直接调用run()方法是同步的,要实现多线程的目的只有使用start()方法。

start()线程的过程 

4.线程的生命周期

1.初始状态:

New,线程被构建,但还没有调用start方法

2.运行状态:

Runnable,就绪和运行两种状态都称为“运行中”

3.阻塞状态:

Blocked,线程进入等待状态,某种原因线程放弃了CPU的使用权

4.等待状态:

Waiting,等待。不带时间

5.超时等待状态:

Time_Waiting,超时等待,超时以后自动返回。带有超时时间的等待。

6.终止状态:

Terminated,当前线程执行完毕

Runnable(准备) -> Waiting: object.wait()、object.join()、locksupport.park()

Waiting -> Runnable(运行): object.notify()、object.notifyAll()、locksupport.unpark(Thread)

Runnable(准备) -> Timed_Waiting: object.wait(long)、thread.sleep(long)、thread.join(long)、locksupport.parkNanos()、locksupport.arkUntil()

Time_Waiting -> Runnable(运行):object.notify()、object.notifyAll()、locksupport.unpark()

Blocked状态只有进入synchronize方法或代码块

查看线程 jdc

5.线程的基本操作

1.Thread.join

是保证线程执行结果的可见性,也就是说会保证使用join方法的线程优先执行。

2.Thread.sleep

线程暂停执行,直到等待的时间结束才恢复执行或在这段时间内被中断。

3.Thread.yield

使当前线程从执行状态(运行状态)变为可执行态(就绪状态),就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。

工作流程:

1.挂起线程修改运行状态

2.sleep()方法设定定时器

3.结束时触发定时器,内核修改运行状态。

PS:Thread.sleep(0)的意义,进行CPU的竞争,使CPU重新分配资源。

3.Thread.wait和Thread.notify

使用它们可以解决:生产者消费者模式,wait会释放锁

PS:为什么要加synchronized关键字 wait和notify方法是互斥存在的,本质是一种条件的竞争,所以需要加。

4.关闭线程的方式

线程友好结束的条件是Run方法中的方法结束,即run()方法结束后线程就会被终止。

Thread.stop()方法不建议使用,可能会导致数据不完整,运行不完整。

1.使用共享变量进行数据的交互,Thread.currentThread().isInterrupter()方法

2.Thread.interrupt()方法 通知线程中断,把共享变量从false设置为true

3.Thread.interrupted()方法,获取标志位并且手动复位

5.线程的复位

jvm进行复位

手动、被动复位

6.线程安全问题

线程安全:当多个线程访问某个对象时,不管运行时环境采用何种调度方式活着这些线程如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类是线程安全的。

线程的三大特性:原子性、可见性、有序性

如何解决可见性有序性问题

本质是存在缓存和指令重排序的问题。

加锁,本质是共享了资源。

Volatile、synchronized、final关键字

1.synchronized

范围:对于普通同步方法,锁是当前实例对象

           对于静态同步方法,锁是当前类的Class对象

           对于同步方法块,锁是Synchonized括号里配置的对象

实例锁(对象实例)、类锁(静态方法、类对象)、代码块锁

img

 锁的储存:

锁的升级:jdk1.6版本后

无锁-》 偏向锁-》轻量级锁(cas自旋)-》重量级锁

 偏向锁无法存储hashcode

如何打印锁的信息

2.volatile关键字

共享数据在多线程情况下保证可见性,解决可见性和有序性的问题  实质是 #Lock指令禁止高速缓存

Store Barried、LoadBarrier、Full Barrier(读、写、复合)

指令重排序问题可用读写屏障解决

其实质:

在CPU层面上,存在高速缓存 会导致数据一致性的问题。解决:[总线锁]-》[缓存锁]

缓存锁:缓存一致性协议

MSI、MESI、MOSI....

Modify(修改) 只缓存在当前Cpu与主内存数据不一致、Exdusive 独享,数据只存在在当前Cpu并且没有被修改、Shared(分享) 数据存在多个Cpu,与主内存一致、Invalid(失效)数据在这个Cpu是失效的。 

优化缓存一致性协议:

异步,等待或通知时,继续执行。等待或失效完成后,再更新。

Store Bufferes + Invalid Queue,但会出现指令重排序问题

内存屏障指令:

使得Store Bufferes + Invalid Queue失效

 

3 final关键字 

提供了内存屏障的规则

2.Happens-Before原则

前面一个操作对后续操作是可见的。 

1.程序顺序规则

2.监视器锁规则

3.Volatile变量规则

4.传递性规则

5.start()规则

6.Join()规则

举报

相关推荐

0 条评论