多线程与高并发(上)

155 阅读5分钟

概念

进程

普通版:进程是程序运行起来的状态。 专业版:进程是OS分配资源的基本单位。

线程

普通版:线程是一个程序的执行路径。 专业版:线程是执行调度的基本单位。

纤程

用户态的线程,线程中的线程,切换和调度不需要经过OS(优势占有资源少,切换简单,性能高)

线程API

如何启动一个线程

1:Thread 2: Runnable 3:Executors.newCachedThrad

深入理解线程的几个方法

  • run():创建一个线程执行对应的方法(同步),但是主线程阻塞等待run()执行完毕
  • start():创建一个新的线程去执行(异步),但是主线程是非阻塞的可以并行处理。
  • sleep():睡眠
  • wait():等待
  • yield():释放线程,但是释放后对和其他线程公平竞争cpu的使用权,可能会再次获得cpu使用权
  • join():线程1,和线程2,如果在线程1中 2.join(),那么1将会在2执行完毕后继续执行最后结束,符合volatile的happen before原则
  • notify(),notifyAll():唤醒正处于等待状态的锁

线程的状态

	java线程在运行的声明周期中可能处于6种不同的状态,在给定的一个时刻,线程只可能处理其中的一个状态。
状态名称说明
New初始状态,线程被构建,调用start()方法之前的状态
Runnable运行状态,java中运行状态包括就绪(ready)和运行(runnable)两种状态统称运行状态
Time_Waiting超时等待状态,和waiting不同,等待一定时间后,会自动切换状态到runnable
Waiting等待状态,表示线程进入等待状态,需要手动切换状态(通知或者中断)
Blocking阻塞状态,等待获取锁执行同步方法或者同步代码块会进入阻塞状态
Terminated终止状态,表示当前线程执行完毕

注意:java.concurrent包中的Lock()接口线程状态是等待状态,因为Lock接口对于阻塞实现均使用了LockSupport.park()方法

手写DCL

public class Singleton{
    private static volatile Singleton singleton = null;
    
    public static Singleton getInstance(){
        if(singleton == null){
            synchronized(Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
            return singleton;
        }
    
    }

}

volatile

  1. 可见性(必然的),原子性(非必然)。
  2. 8个happens-before原则,4个内存屏障原则。
  3. 底层引入了缓存一致性协议mesi协议 写时处理器会将缓存写回主村,导致其他处理器的缓存失效
  4. JMM模型

synchronized

  1. synchronized的有几种形式的锁,但是根本就有两种, 类对象的锁和当前对象的锁。static方法锁的是类对象
  2. synchronized锁升级的过程要非常了解 偏向锁(打标记记录,其实不是锁)-->CAS锁(自旋锁,无锁化,自旋消耗cpu资源,一般默认自旋10次升级)-->锁(真正的加了锁)
  3. synchronized锁只有锁升级的概念,锁不会降级。

详细介绍飞机票 juejin.cn/post/690490…

LongAdder

LongAdder是对AtomicLong进行的优化升级类,能够支撑更高的并发。

LongAdder的原理是,底层分为多个数组同时加减,能够更充分的利用cpu,减少资源浪费,最后把多个结果合并。

ReentrantLock

这里我们简单的说下ReentrantLock,以前我们都是推荐使用这个锁的,他更加的灵活,轻量化我们自己能够控制,但是现在随着synchronized的优化并不推荐使用了,反而更加推荐synchronized。

几个重要的API

  1. 手动控制加锁解锁 lock()-->unlock()
  2. 创建可以被打断的锁lockInterruptibly()-->interrupt()
  3. 公平锁,非公平锁概念,公平锁:每一个线程来了都是公平的排队进行锁的获取,非公平锁:线程过来可以插队各凭本事进行锁的获取,谁拿到算谁的。

CountDownLatch 和 CyclicBarrier

CountDownLatchCyclicBarrier
减计数方式加计数方式
计算为0时释放所有等待的线程计数达到指定值时释放所有等待线程
计数为0时,无法重置计数达到指定值时,计数置为0重新开始
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用可重复利用

Phaser

Phaser(阶段器),用来解决控制多个线程分阶段共同完成任务的情景问题。 例如有这样的一个题目:5个学生一起参加考试,一共有三道题,要求所有学生到齐才能开始考试,全部同学都做完第一题,学生才能继续做第二题,全部学生做完了第二题,才能做第三题,所有学生都做完的第三题,考试才结束。分析这个题目:这是一个多线程(5个学生)分阶段问题(考试考试、第一题做完、第二题做完、第三题做完)

Semaphore

作用

Semaphore是一个计数信号量,必须由获取他的线程释放,一般用于限流。 初始化,Semaphore semaphore = new Semaphore(3); 获取,semaphore.acquire(); 释放,semaphore.release();

ReadWriteLock

读写锁,读锁也成为共享锁,写锁也叫做排他锁。读锁可以实现并发读,这样能够提高读取效率,并且不允许被修改,也就是进行写操作,写锁,只允许一个线程对其进行修改操作。

    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();

Exchanger

线程交换器,他可以把线程内的data进行交换。