1.多线程-了解多线程与高并发

时间:2022-06-24
本文章向大家介绍1.多线程-了解多线程与高并发,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

并发与并行的区别:

并发:两个任务或者多个任务执行,多个任务交替执行
并行:两个任务或者多个任务一起同时执行
例子:

一个CPU,去执行一个多线程任务。是不可能并行的,一个CPU只能执行一条命令,CPU会高速的切换线程任务去执行。这种情况下线程是并发的。 一个系统中拥有多个CPU,执行多线程任务,多个CPU会同时执行任务,这种情况是并行。并行也只可能出现在多核CPU中。 两者虽然本质不同,但是造成的最终效果是一样的。没有太必要做详细的区分。

临界区:

临界区用来表示一个共享资源,可以被多个线程使用,但是每一次,只能有一个线程去使用。相等于厕所的坑。

阻塞非阻塞:

假如有两个线程,把他们当作两个人,都拉肚子,只有一个厕所,两人都必须要上这个厕所。

阻塞:

一个人先进去了,另外一个人在等待,另外一个人必须要等第一个人出来,才能进去搞事情,这个就是阻塞行为。

非阻塞:

强调没有一个线程可以妨碍其他线程去执行任务。所有线程都会尝试不断前向执行。后面会有更详细的描述。

死锁,饥饿,活锁

死锁:

一个单行道,有一个车,正在往前驾驶,然后有一个车从反方向驾驶过来,这两个谁都不想退,那么这个状态将会一直这样维持下去。线程如果发生这种类似的行为,那么就可能会造成死锁。

饥饿:

某个线程或者多个线程因为某些原因无法获得所需要的资源,比如有一个特大的饼,有很多人都想要吃,武力值高的肯定先吃,但是武力值高的人太多,导致那些武力值极低的人,会一直吃不到,这个就像等于线程中的优先级,优先级高的优先去做某事。跟死锁相比,饥饿还是会在某一段时间解决的,比如武力值高的都吃饼吃饱了,就到武力值低的去吃了。

活锁:

过马路的时候可能碰到这种情况,你往前走,有一个人骑着自行车往你这个方向驶来,他看到你了,你看到他了,你想让他,他想让你,你往左,他往右,你往右,他往左,两个一直保持着礼让的态度,就会一直这样来回重复做这样的事情。现实中还好说,人可以去交流,几次过后就解决了,但是对于线程,如果没有给线程赋予这种处理的思路,它就可能一直重复和另一个线程做这种“礼让”的事情,导致没有一个线程可以同时拿到所有资源去正常执行任务。

并发级别:

由于临界区的存在,我们必须控制多线程间的并发,根据控制并发的测率,我们可以把并发的级别进行分类,大致上可以分为:阻塞~无饥饿~无障碍~无锁~无等待五种。

阻塞:

java中的synchronized关键字,都会试图去得到临界区的锁,如果得不到,线程就会被挂机等待,知道占有了资源为止。是一种悲观策略,认为肯定会有线程去抢资源,一个线程抢到资源,其他的就会挂起等待执行完毕,再去试探。

无饥饿:

公平锁,不管优先级多高,讲究的是先来后到,这样所有的线程都有机会去执行。

无障碍:

是一种乐观策略,认为不会有线程去抢资源,无障碍的去执行,如果检测到冲突,就回滚。

无锁:

保证有一个线程能在有限步内完成操作离开临界区。

无等待:

要求所有线程必须在有限步内完成操作。

线程的三个特性:

原子性:

指的是一个操作不可中断,哪怕多个线程同时操作一个变量,每个线程只改它要改的值,不管被其它线程修改成什么值。

可见性:

并行多线程修改了某一个共享变量的值,其他线程并不一定能够立即知道这个修改。

有序性:

程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的,在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到串行程序的执行,却会影响到多线程并发执行的正确性。

指令重排:
 1/**
 2 * @program: test
 3 * @description: 
 4 * @author: Mr.Yang
 5 * @create: 2018-11-06 11:22
 6 **/
 7public class A {
 8    int a=0;
 9    boolean flag=false;
10    public void  write(){
11        a=1;
12        flag=true;
13    }
14    public void reader(){
15        if(flag){
16            int i =a+1;
17        }
18    }
19}

假设线程A先执行writer方法,接着线程B执行reader()方法,如果发生指令重排,线程B在第11行代码,不一定能看到a被赋值1了。

指令重排的原因:

提高性能。可以共同执行任务,不需要等待第一个执行完成之后再去执行另一个任务。如果做成要任务执行完成再去执行另外一个,那么就和串行无太大区别了,性能也会严重受损。

一些指令是不能重排的:Happen-Before规则
程序顺序原则:
  • 一个线程内保证代码正常顺序执行
  • volatile规则:volatile变量的写,先发生与读,保证了volatile的可见性
  • 锁规则:解锁必然发生在随后的加锁前
  • 传递性:A引用B,B引用C,那么C必然先在A前执行。
  • 线程的start()方法先于它的每一个动作
  • 线程的所有操作先于线程的完结
  • 线程的中断先于被中断线程的代码
  • 对象的构造函数执行和结束先于finalize()方法