java并发编程

线程回顾

线程基本概念

程序: 静态的代码

进程: 运行中的程序,被加载到内存中,是操作系统分配内存的基本单位

线程: 线程是程序处理的基本最小单位 ,是cpu执行的单元

线程的创建方式:

类 继承 Thread 重写run() 创建类的对象

实现Runnable接口 重写run() 任务 new Thread(任务)

实现Callable接口 重写call() 有返回值,可以抛出异常

线程的状态

1. 新建(NEW):新创建了一个线程对象。

2. 就绪:线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

3. 运行:可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

4. 阻塞:阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu,暂时停止运行。直到线程进入可运行状态,才有机会再次获得cpue 转到运行)状态。 

5. 死亡:线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生

线程中的常用方法

getName(); 返回线程的称

start(); 启动线程
setName() ;设置线程的名称
setPriority(); 设置线程的优先级 (并不是优先级高的线程一定先执行 要由CUP的调度决定)
线程优先级最大为10,最小为1,默认为5
join() ; 等待线程终止
currentThread() ;返回对当前正在执行的线程对象的引用
sleep(long millis) 让当前正在执行的线程休眠(暂停执行)
yield() ;线程让步 让出CPU的执行权 进入就绪队列,等待CPU的调用
isAlive(); 测试线程是否在进行;

wait此方法会使当前线程就进入阻塞状态,并释放同步监视器。
notify此方法会唤醒被wait的一个线程。如果有多个线程被wait, 就唤醒优先级高的那个。
notifyAll一旦执行此方法,就会唤醒所有被wait的线程

多线程

多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。比如程序实现一些需要等待的任务时,比如用户输入,文件读写操作,网络操作,搜索等。

多线程的优点: 提高程序的响应,提高CPU利用率,改善程序结构,将复杂任务分为多个线程,独立运行。

多线程的缺点:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;多线程需要协调和管理,所以需要cpu时间跟踪线程;线程之间对共享资源的访问会相互影响。

并发编程

并发与并行

单核 cpu 下,线程实际是串行执行的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片,分给不同的线程使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是同时运行的。多核 cpu 下,每个核(core)都可以调度运行线程,这时候线程可以是并行的。
并发 说的是在 一个时间段内 ,多件事情在这个时间段内 交替执行
并行 说的是多件事情在 同一个时刻 同事发生。

java内存模型(JMM)

java内存模型,是java虚拟机规范的一种工作模式.将内存分为主内存和工作内存.变量数据存储在主内存中,线程在操作变量时,会将主内存中的数据复制一份到工作内存.在工作内存中操作完成后,再写回到主内存中.

并发编程的核心问题

1.不可见性

一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性。 但对于如今的多核处理器,每个CPU 内核都有自己的缓存,而缓存仅仅对它所在 的处理器内核可见,CPU 缓存与内存的数据不容易保证一致。 
2.无序性
为了优化性能,有时候会改变程序中语句的先后顺序。 而改变执行顺序 可能会造成后续的逻辑错误。
3.非原子性
Java 并发程序都是基于多线程的,自然也会涉及到任务切换,任务切换的 时机大多数是在时间结束的时候。我们现在基本都使用高级语言编程,高级语 言里一条语句往往需要多条 CPU 指令完成。如 count++,至少需要三条 CPU 指令。
指令 1:首先,需要把变量 count 从内存加载到工作内存;
指令 2:之后,在工作内存执行 +1 操作;
指令 3:最后,将结果写入内存; 还是以上面的 count++ 为例。两个线程 A 和 B 同时执count++即便 count 使用 volatile 修辞,我们预期的结果值是 2,但实际可能是 1。

 那么如何解决以上问题呢:

        缓存 导致的 可见性问题 编译优化 带来的 有序性问题 线程切换 带来的 原子 性问题 。那么如何解决该问题呢?

volatile关键字

在JAVA中用volatile 关键字来解决可见性和有序性的问题一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后:
1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变 量的值,这新值对其他线程来说是立即可见的。
2. 禁止进行指令重排序。
3. volatile 不能保证对变量操作的原子性

volatile底层实现原理

有序性问题:volatile 修饰的变量在操作前,添加内存屏障,禁止指令对其进行重排序.

可见性问题:volatile通过 Lock 前缀指令 + MESI 缓存一致性协议来实现可见性。JVM 会发送一个 Lock 前缀指令给 CPU,CPU 在执行完写操作后,会立即将新值刷新到内存,同时因为 MESI 缓 存一致性协议,其他各个 CPU 都会对总线查看,看自己本地缓存中的数据是否被别人修改,如果发现修改了,会把自己本地缓存的数据丢弃掉。然后这个 CPU 里的线程在使用变量时,就会从主内存里加载最新的值了,这样就保证了可见性

原子性问题

volatile关键字可以解决可见性和有序性问题 但无法解决由线程切换带来的原子性问题,那么原子性问题该怎样解决呢?

如何保证原子性
“同一时刻只有一个线程执行”我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的那么就都能保证原子性了。
同一时刻只能有一个线程执行我们会想到锁。锁是一种通用的技术方案,Java 语言提供的 synchronized 关键字,就是锁的一种实现。

synchronized是独占锁/排他锁(就是有你没我的意思),synchronized并不能阻止CPU时间片的切换,只是当其他线程要访问这个资源时,发现锁还未释放,所以只能在外面等待。synchronized一定能保证原子性,因为被synchronized修饰某段代码后,无论是单核CPU还是多核CPU,只有一个线程能够执行该代码,所以一定能保证原子操作。

除了加锁我们还可以使用原子变量解决原子性问题。

原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量的原子操作。只是synchronized的成本相对较高,需要获取锁对象,释放锁对象,如果不能获取到锁,还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量的原子操作,建议使用原子变量。

原子类

在java中还提供一些原子类,是一种无锁实现.采用CAS机制(CAS(Compare-And-Swap) 比较并交换)是一种无锁实现.在低并发情况下使用.

CAS

CAS是一种机制  是乐观锁的一种实现方式,他采用的是自旋锁的思想,是一种轻量级的锁机制。 即每次判断我的预期值和内存中的值是不是相同,如果不相同则说明该内存值 已经被其他线程更新过了,因此需要拿到该最新值作为预期值,重新判断。而该线程不断的循环判断是否该内存值已经被其他线程更新过了,这就是自旋的思想

CAS 包含了三个操作数:
①内存值 V
②预估值(期望值) A (比较时,从内存中再次读到的值)
③更新值 B (更新后的值)
当且仅当预期值 A==V(表示内存中的值没有被改变过),将内存值 V=B(内存中的值没有改变过 可以将新的值更新到内存中),否则什么都不做。 这种做法的效率高于加锁,当判断不成功不能更新值时,不会阻塞,继续获得 cpu执行权,继续判断执行。

 CAS的缺点

CAS 使用自旋锁的方式,由于该锁会不断循环判断,因此不会类似 synchronize 线程阻塞导致线程切换。但是不断的自旋,会导致 CPU 的消耗,在并发量大的时候容易导致 CPU 跑满。
ABA问题 
ABA 问题,即某个线程将内存值由 A 改为了 B,再由 B 改为了 A。当另外一个 线程使用预期值去判断时,预期值与内存值相同, 当前线程的 CAS 操作无法分辨 当前 V 值是否发生过变化 如图所示

解决 ABA 问题的主要方式,通过使用类添加版本号,来避免 ABA 问题。如原先的内存值为(A,1),线程将(A,1)修改为了(B,2),再由(B,2)修改 为(A,3)。此时另一个线程使用预期值(A,1)与内存值(A,3)进行比较,只需要比较版本号 1 和 3,即可发现该内存中的数据被更新过了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值