1.多线程-了解多线程与高并发
Posted JAVA知识总结与分享
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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()方法
以上是关于1.多线程-了解多线程与高并发的主要内容,如果未能解决你的问题,请参考以下文章