文章目录
- 线程、异步和并发
- 并发问题
- 并发级别
- 并发编程特征
- Happen-Before原则
- 线程生命周期
- 线程优先级
- 守护线程
- 信号量
- 倒计数器
- 循环栅栏
线程、异步和并发
- 程序:为完成某个任务、用某种语言编写的一系列指令的集合,是静态的代码、静态的对象
- 进程:程序执行的过程,即正在执行的程序,是动态的、有生命周期的
- 线程:进程可进一步细分为线程,是程序内部的一条执行路径,如电脑管家杀毒、清理垃圾,就是两个
- 单核CPU:只有一个CPU,在同一个时间片内,只执行一个线程。通过时间片轮转,在不同线程之间迅速地切换线程,让人能感觉在同时执行多个线程。
- 多核CPU:在同一个时间片内,有多个CPU执行多个线程
- 同步:调用方法返回结果后,才能继续后续的行为
- 异步:调用方法立即返回,开始另一个方法,当真实的返回结果出现后会通知调用者。
同步就像线下购物,要先在商城选择商品,在收银台付账后自己把商品运回家。
异步就像线上购物,不需要到商城,在家里下单后自己就可以做别的事去了,由商家负责联系厂商,然后发货运到家。货运到家后,商家给你打电话,你就知
- 并发:利用时间片轮转执行多个任务,即一个程序执行一段时间后,换另一个程序执行。
- 并行:多个程序同时执行,只能在多核CPU中运行。
如果是多个线程都需要处理的数据,就放在临界区。每一次只能一个程序来使用,其他线程想要使用就必须等待。
并发问题
- 阻塞:当一个线程处理一个数据时,在其释放资源之前,其他线程就会被挂起等待。是一种悲观策略,常用synchronized关键词。wait、join、yield、sleep方法都可以导致阻塞。
- 非阻塞:当一个线程的执行不会阻碍其他线程的执行,发生冲突时,线程回滚。
- 死锁(deadlock):当所有的线程都在处理同一个数据时,相互占用资源导致所有的线程都无法执行下去。就像道路上所有汽车占着别人的道,导致所有车辆都无法移动。
- 饥饿(starvation):程序总是在执行级别高的线程,导致级别低的线程无法获得资源。
- 活锁(livelock):多个线程处理同一个数据时,都主动释放资源给其他线程使用,导致没有一个线程能处理数据。就像迎面走来一个人,你们互相谦让,导致谁也无法前进。
并发级别
- 阻塞
- 无饥饿(starvation-free):使用非公平锁,级别高的线程就会插队;使用公平锁,无论级别高低,所有的线程都需要排队
- 无障碍(obstruction-free):
当一个线程进入临界区后,其他线程也可以进入
是一种乐观策略,认为线程发生冲突的概率不大。
各个线程一旦发生冲突,会回滚对自己做的修改。
可能会发生所有线程一起回滚的情况,导致没有一个线程能走出临界区。
这种策略通常需要使用一致性标记,在线程进入临界区前,会读取并保存这个标记,随后再次读取这个标记。如果标记没有被修改,那么标记一致,线程进入临界区,否则就表示有线程冲突,再重新操作。 - 无锁(lock-free):
所有线程都尝试访问临界区。
如果有线程没有成功获取资源,就会继续尝试,直到成功为止。
有可能出现修改不成功的线程无穷循环的情况,但能保证至少有一个线程可以走出临
并发编程特征
- 原子性:即使多个线程一起执行,任一线程的操作都不会打断。
*long型数据的读写不具有原子性 - 可见性:如果一个线程修改了结果,那么其他线程都应该知道。
- 有序性:保证写在前面的方法先执行,写在后面的后执行。
*有时了优化CPU的操作,会进行指令重排,影响执行顺序。
Happen-Before原则
这是指令重排的原则
- 程序顺序原则
- volatile原则:volatile变量的写优先于读,保证可见性
- 锁规则:同一把锁的解锁的操作要发生在下次加锁动作的前面
- 传递性:A先于B,B先于C,那么A先于C
- 线程的start()方法先于它的每一个操作
- 线程的所有操作都先于它的终结
- 线程的中断先于被中断线程的代码。
- 对象的构造函数的执行、结束先于finalize()方法
线程生命周期
线程优先级
从1到10,一般默认为5,优先级高更高的概率执行,但不一定就是先执行。
Thread类里有最大、最小和默认的常数优先级变量。
守护线程
类似于垃圾回收线程在Java中的作用,在start()方法之前使用setDaemo(),设置为true,否则就会变成用户线程。
举例:
class DaemonDemo extends Thread{
@Override
public void run(){
while (true){
System.out.println("I am alive");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//调用守护线程
public void test(){
Thread t = new DaemonDemo();
//设置为守护线程
t.setDaemon(true);
t.start();
}
信号量
确定可以同时访问某个资源的线程的数量
举例:
class SemaphoreDemo implements Runnable{
final Semaphore semaphore = new Semaphore(5);
@Override
public void run(){
try {
//尝试获取许可,如果无法获得,就会等待
semaphore.acquire();
//如果无法获得许可,立即返回false,不等待
semaphore.tryAcquire();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
}
倒计数器
规定数量的线程执行完后,再去执行主线程。
举例:
class CountDownLatchDemo implements Runnable{
//10个线程倒数完后,才会执行主线程
static final CountDownLatch end = new CountDownLatch(10);
public static CountDownLatch getEnd() {
return end;
}
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10)*1000);
System.out.println("check");
//一个线程执行完后,倒计数器减一
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void test(){
CountDownLatchDemo demo = new CountDownLatchDemo();
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i=0; i<10; i++){
exec.submit(demo);
}
try {
CountDownLatchDemo.getEnd().await();
System.out.println("fire");
exec.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
循环栅栏
相当于可重用的倒计数器
举例:
ier.await();
doWork();
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
void doWork(){
System.out.println(solider+":任务完成");
}
}
class BarrierRun implements Runnable{
boolean flag;
int N;
BarrierRun(boolean flag, int N){
this.flag = flag;
this.N = N;
}
@Override
public void run() {
if (flag){
System.out.println("司令:士兵"+N+"个,任务完成!");
}
else{
System.out.println("司令:士兵"+N+"个,集合完毕!");
flag=true;
}
}
}
public void test(){
final int N=10;
Thread[] allSolider = new Thread[N];
boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(N,new BarrierRun(flag, N));
System.out.println("集合队伍");
for (int i=0; i<N; ++i){
System.out.println("士兵"+i+"报道");
allSolider[i] = new Thread(new CyclicBarrierDemo(barrier,"士兵"+i));
allSolider[i].start();
}
}