多线程!JUC并发编程!锁机制!

什么是线程和进程?线程与进程之间的关系?并发与并行的区别?为什么要用多线程?

1.进程是计算机操作系统运行的基本单位,进程也就是程序执行一次的过程(在我们的Java中,当我们启动一个main函数的时候就相当于启动了一个JVM 进程,而main函数所在的线程也就是我们说的主线程

                               

2.线程就是更小的执行单位了 一个进程可以有多个线程,同时多个线程之间共享进程的堆和方法区;同时每个线程拥有自己独立的本地方法栈,虚拟机栈以及程序计数器;

3.并发(同一时间段内,做多个任务)与并行(同一时刻,做多个任务)

4.为什么需要多线程?提高程序执行效率和响应速度!

为什么程序计数器,本地方法栈和虚拟机栈是私有的?堆和方法区的作用是什么?

程序计数器:线程切换后能过恢复到正确的位置

1.在多线程的情况下,记录当前线程所运行的位置,从而当线程被切换回来的时候才能够知道该线程上次运行到了哪里

2.字节码解释器通过改变程序计数器来依次读取指令(顺序执行,循环,选择等),从而实现代码的流程控制

虚拟机栈和本地方法栈:保证线程中的局部变量不被别的线程访问到

1.虚拟机栈的作用是每个Java方法在执行的时候需要创建一个栈帧来存储局部变量,操作数栈,常量池的引用;同时每一个方法的调用和完成都代表着一个栈帧在Java虚拟机栈中入栈和出栈的过程

2.本地方法栈和虚拟机栈的作用差不多;本地方法栈主要是为虚拟机使用到的Native方法服务;

堆和方法区:

1.堆是进程中最大的一块内存,主要是用于存储新创建的对象(所有的对象都是在堆分配内存)

2.方法区主要是用于存放已被加载的类信息,常量,静态变量以及编译器编译后的代码

上下文切换与线程死锁

1.什么是上下文切换:当前任务在执行完CPU的时间切片切换到其他任务之前会先保存自己的状态,以便下次切换到这个任务的时候可以再加载到这个任务的状态(任务从保存到再加载的过程)

2.什么是线程死锁?如果避免线程死锁?

线程死锁指的是  线程A持有B资源  线程B持有A资源 而他们同时都想申请对方的资源,但是他们都不放弃自己手里的资源,所以就会相互等待从而陷入“死锁”状态

线程死锁的四个条件:1.互斥条件(一个资源只能一个线程把控)2.请求与保持条件(一直请求别人的,自己有的却不放手,真tm狗)3.不剥夺条件(自己线程的资源在没使用完之前不能被其他线程强行剥夺)4.循环等待条件(几个线程之间形成一种头尾相接的循环等待资源关系)

解决方法:1.一次性申请所有的资源 2.申请不到别人的资源时,主动释放自己的 4.按序申请资源

synchronized关键字的使用

1.修饰实例方法 (给当前对象实例加锁):

2.修饰静态方法(锁定当前类的Class对象) 

3.修饰代码块(锁定指定对象)

 双重校验锁实现对象单例(线程安全的情况下)

volatile关键字的作用:

        volatile关键字可以保证多线程环境下对变量instance的可见性,同时在JDK1.5之前,由于指令重排序的原因,上述代码可能会出现问题,因此需要将变量instance声明为volatile类型才能确保其正确性;

JMM(Java内存模型):

                        

1. JDK1.2 之前,Java 的内存模型实现总是从主存(即共享内存)读取变量

2.在当前的 Java 内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中

3.而不是直接在主存中进⾏读写。这就可能造成⼀个线程在主存中修改了⼀个变量的值,⽽另外⼀个线程还继续使⽤它在寄存器中的变量值的拷贝,造成数据的不⼀致

4.volatile 保证变量的可见性,某个线程修改了变量的值,其他线程立即能够看到变量的最新值

5.synchronized 关键字保证线程之间的有序性和排他性,同一时间只有一个线程能够执行代码块;

6.final 关键字可以保证不可变性,即一旦变量被赋值,就不能再修改它的值。

JUC并发编程原子类与AQS!

Atomic原子类:

1.基本类型:整形原子类(AtomicInteger) 长整型原子类(AtomicInteger) 布尔型原子类(AtomicBoolean)

2.数组类型;整形数组原子类(AtomicIntegerArray)长整形数组原子类(AtomicLongArray)

3.引用类型:

4.对象的属性修改类型:

AQS:并发编程工具->主要是用于实现同步器

1.AQS原理:

        核心思想是基于一个共享的抽象队列来实现线程的同步和互斥。在这个队列中,每个节点代表一个正在等待某个锁或者信号的线程,当持有锁或者信号的线程释放锁或者发出信号时,就会从队列中唤醒下一个需要锁或者信号的线程;

这个机制 AQS 是⽤ CLH 队列锁实现的,即将暂时获取不到锁 的线程加⼊到队列中

1. 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作

 

2.AQS 使⽤ CAS 对该同步状态进⾏原⼦操作实现对其值的修改,状态信息通过 protected 类型的 getStatesetStatecompareAndSetState 进⾏操作

2.AQS资源共享方式:

独占模式只有⼀个线程能执行,如 ReentrantLock 。又可分为公平锁和非公平锁;

公平锁:按照请求锁的先后顺序依次获得锁

非公平锁:不保证按照请求锁的先后顺序依次获得锁

共享模式:多个线程可同时执行,如CountDownLatch(后续代码示例分析)

3.AQS底层与AQS组件:

AQS 使用了模板方法模式,⾃定义同步器时需要重写下面几个 AQS 提供的模板方法:

  1. tryAcquire(int) 该方法尝试获取同步状态,并返回一个boolean值表示是否成功获取。在这个方法中,我们需要实现具体的获取同步状态的逻辑,并根据获取结果返回true或false。

  2. tryRelease(int) 该方法尝试释放同步状态,并返回一个boolean值表示是否成功释放。在这个方法中,我们需要实现具体的释放同步状态的逻辑,并根据释放结果返回true或false。

  3. tryAcquireShared(int) 该方法尝试获取共享同步状态,并返回一个int值表示当前同步状态的数量。在这个方法中,我们需要实现具体的获取共享同步状态的逻辑,并根据获取结果返回当前同步状态的数量。

  4. tryReleaseShared(int) 该方法尝试释放共享同步状态,并返回一个boolean值表示是否成功释放。在这个方法中,我们需要实现具体的释放共享同步状态的逻辑,并根据释放结果返回true或false

AQS组件:

Semaphore ( 信号量 )- 允许多个线程同时访问: synchronized ReentrantLock 都是⼀次只
允许⼀个线程访问某个资源, Semaphore ( 信号量 ) 可以指定多个线程同时访问某个资源。
CountDownLatch (倒计时器): CountDownLatch 是⼀个同步⼯具类,⽤来协调多个线
程之间的同步。这个⼯具通常⽤来控制线程等待,它可以让某⼀个线程等待直到倒计时结
束,再开始执⾏

4.CountDownLatch:

作用:允许 count 个线程阻塞在⼀个地方,直至所有线程的任务都执行完毕

场景应用:我们要读取处理 6 个文件,这 6 个任务都是没有执行顺序依赖的任务,但是我们需要返回给用户的时候将这几个文件的处理的结果进行统计整理。

1.定义了⼀个线程池和 threadCount 6 CountDownLatch 对象

2.使⽤线程池处理读取任务,每⼀个线程处理完之后就将 count-1

3.调用 CountDownLatch 对象的 await() 方法,直到所有文件读取完之后,执行后面的逻辑

使用 CompletableFuture 类来优化处理文件的并发性能

 CAS操作:

         CAS操作是一个原子性的操作,它包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。CAS指令执行时,当且仅当内存位置V的值与预期原值A相等时,才将内存位置V的值更新为新值B,否则不做任何操作;

猜你喜欢

转载自blog.csdn.net/weixin_64625868/article/details/131021906