线程与Java中的各种锁

线程

​ 进程是操作系统分配资源的单元,是运行中的程序。

​ 线程:

​ cpu调度的最小单位;

​ 一个具体的执行单元(任务)。

多线程

​ 就是一个进程(程序)内,允许多个线程,同时进行;

​ 可以提高cpu的利用率,增强程序的功能;

​ 对硬件(cpu,内存,硬盘)的要求提高;

​ 多线程访问同一个共享资源,线程安全问题。

线程创建方式

​ 继承Thread,

​ 实现Runnable接口 run();

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

线程状态

新建

​ start()

就绪

​ yield() cpu加载

运行

​ sleep()

​ wait()

​ join()

​ IO

​ 等待同步锁

阻塞

​ stop()

​ 任务结束

死亡

在这里插入图片描述

多线程安全问题

​ 卖票,取款,秒杀,抢购;

​ 解决: 加锁,排队

守护线程

线程间的通信

并发编程

​ 并行:同时做多件事

​ 并发:同一时间,如何应对多件事

多线程访问同一个共享资源,产生线程安全问题

​ 现在的cpu使多核的,可以同时执行多个线程

​ 安全问题

​ 1.性能

​ 2.死锁

​ 3.可见性,有序性,原子性

​ cpu–>内存–>IO(硬盘)

​ 三者之间的读写速度有差别

​ cpu提供缓存

​ 任务细化到线程,切换执行

​ cpu对我们指令代码的顺序进行优化(重排)

​ 为了线程使用数据更快速,会把线程中使用到的变量复制到线程的工作内存中。

​ 线程间的数据是不可见的;

​ JMM(Java Memory Model)内存模式:

在这里插入图片描述

线程安全问题

可见性

​ 不同线程中,有一个缓存,缓存要操作的变量;

​ 为了提高效率,会等所有操作完成后,在将数据写入到主内存中;

​ 当数据还未写入到主内存时,其线程对修改数据不可见。

有序性

​ cpu在执行过程中,可能会对我们代码执行的顺序做出优化,重新排列指令。

原子性

​ 多线程在多核cpu中运行,线程切换会造成打破原子性;

​ 一行代码,本来是应该不被拆分执行的,但是受线程切换导致其他线程执行的影响,最终运行的结果与预期值可能会不一致。

总结:

缓存导致可见性问题,线程切换带来的原子型问题,编译优化带来的有序性问题。

Volotlie

​ 1.修饰后的变量,在一个线程操作后,对其他线程立即可见。

​ 2.修饰后的变量,禁止指令重排序。

​ 3.不能解决原子性问题

保证原子性

​ 保证共享变量的修改是互斥的(从并行—>并发)

加锁

​ 互斥,同一时间只能有这一个线程访问共享变量;

​ **synchronized:**可以保证在一个线程执行时,其他线程不能操作其共享变量;保证原子性,可见性和有序性。

JUC–原子变量

​ 原子类的原子性是通过volatile + CAS实现原子操作的

加锁是一种阻塞式方式实现;

原子变量是非阻塞式方式实现。

CAS

​ Compare and Swap:比较并交换

​ 采用的是乐观锁与自旋锁;

​ 每次先将数据加载至工作内存 V;

​ 再次从内存中读取其值 A;

​ 如果A==V,则主存数据没有其他线程修改,此时就把计算后的数据B写入内存中。

​ 是一种无锁实现方式,非阻塞式的;

​ 适合低并发;

​ 缺点:

​ 非阻塞式的,其他线程依就可以执行,还要自旋,导致cpu消耗比较大;

​ ABA问题:先将A改为B,再将B改为A,导致其他线程出现误判,内存值与预期值一致;

​ 解决ABA问题:可以通过添加版本号,每次修改后更改版本号,可以通过比较版本号来确定是否发生了更改。

ConcurrentHashMap

​ 是线程安全的;

​ Hashtable也是线程安全的,直接将整个put()方法加锁,锁粒度大,效率低。

​ JUC包中提供了一些用于高并发且线程安全,效率高的类与方法;

​ 放弃分段锁(将某个段整体加锁)而采用了CAS原则+cynchronized;

​ 在put时,先判断添加元素是否是第一个节点;

​ 如果是,则采用CAS原则(比较判断),加入数据到第一个节点;

​ 如果不是,会用第一个节点作为锁,添加synchronized锁机制,加锁

保证安全。

java中的锁

​ 不是真实的锁,只是锁的设计或特性

乐观锁:
​ 采用CAS原则,更新数据时,进行判断而已,不加锁;

悲观锁:

​ 采用加锁更新数据,加锁synchronized或Lock;

公平锁:

​ 就是等待锁的线程按照顺序排队,一旦锁释放了,那么排在第一位置的线程获得锁,执行;

非公平锁:

​ 不用排队,释放后,那个线程抢到就先执行;

可重入锁(递归锁):

​ 当线程获取到外层方法锁对象时,依然可以获得内部同步方法的锁,可以进入内部同步方法的锁,可以进入内部方法,否则会出现死锁。

读写锁(ReadWriteLock): 实锁

​ 可以多个同时读;

​ 写数据时必须只有一个执行。

分段锁:

​ 例如jdk8以前的ConcurrentHashMap,分段加锁,提高效率。

自旋锁:

​ 不断重试去抢占cpu,不会阻塞线程,但数量过多后就会很消耗cpu。

共享锁:

​ 多个线程可以共享一把锁;

独占锁(互斥锁):

​ 一次只能有一个线程持有该锁;

​ **AQS(**AbstractQueuedSynchronized):

​ 抽象队列同步器,没有获得锁的其他线程,把他们加入到一个队列中(阻塞状态,cpu不再加载),当所释放后,队列中的第一个线程将会获得锁,按照顺序排队获取。

锁的状态:

​ 偏向锁:

​ 只有一个线程,一直获得锁对象,此时锁对象中的锁状态改为偏向锁,并记录线程ID;当同一个线程再次访问时便会直接获得该锁,效率高。

​ 轻量级锁:

​ 当第二个线程访问时,偏向锁将会升级为轻量级锁,其他的线程将会进行自旋,尝试获取锁,不会阻塞,提高效率(线程数量少)。

​ 重量级锁:

​ 当前为轻量级锁时,并发访问量增多,锁的状态也升级为重量级锁,其他线程进入阻塞状态,不再尝试自旋。

synchronized实现

​ 在synchronized修饰的程序块前后会添加一个监视器(进入,推出),利用对象头来记录锁是否被使用;

​ 获取锁,计数器+1,退出监视器,释放锁,计数器-1.

​ 特点:

​ 使用一个唯一的对象,作为锁状态的标记。

ReentrantLock

​ 在内部有一个锁的状态,默认状态为0,如果有线程获取到了锁,将状态改为1;

​ 其他线程有两种处理方式:

​ 公平锁与非公平锁;

​ 如果使用公平锁,就会将等待线程添加到同步等待队列中。

ThreadLocal

​ 为每个线程复制一份变量副本;

//创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭
    private  static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {//定于初始值
            return 0;
        }
    };

​ 底层使用的是一个ThreadLocalMap对象存储;

​ 会存在内存泄漏的问题:

​ 使用ThreadLocal对象作为键,是弱引用,可以被回收掉,键为null;但是value值为强引用.

​ 建议: 在使用结束后,主动调用remove()方法删除

线程池

​ 为了减少频繁创建销毁线程开销

​ 可以使用线程池, 于心啊创建一部分线程, 重复使用, 减少创建销毁开销

​ jdk5自带创建线程池的类:

​ ThreadPoolExecutor:

​ 有七个参数:

​ corePoolSize: 核心线程数量(不会被销毁)

​ 1.prestartAllCoreThread()与prestartAllCoreThreads()方法, 可以预先创建核心数量个线程;

​ 2.起初不会创建, 直到有任务到达时创建, 直到达到核心数量个线程.

​ maximumPoolSize: 空闲多久最大线程数量;

​ keepAliveTime: 超出核心数量部分的线程,在没有执行任务时, 空闲多久后销毁;

​ unit:keepAliveTime : 时间的属性

​ workQueue: 一个阻塞队列,用来存储核心线程之后的线程

​ threadFactory: 创建线程

​ handler: 拒绝任务后的处理

执行过程:

在这里插入图片描述

​ 例如: 核心线程:5 队列:5 线程池最大线程数量:5

​ 开始有任务到达,创建核心线程;

​ 当任务到达6时,核心线程已满,将创建线程进入队列;

​ 当任务到达11时,队列已满,创建非核心线程;

​ 当任务到达16时,已超过最大线程数量与队列,执行拒绝策略.

关闭线程池:

​ shutdownNow: 对正在执行的任务进行interrupt()停止执行;未开始的全部取消,返回未开始的列表;

​ shutdown: 不会强行终止已提交或正在执行的任务,但也不会接受新的任务

orkQueue: 一个阻塞队列,用来存储核心线程之后的线程

​ threadFactory: 创建线程

​ handler: 拒绝任务后的处理

执行过程:

[外链图片转存中…(img-hCfQi8CD-1632961659999)]

​ 例如: 核心线程:5 队列:5 线程池最大线程数量:5

​ 开始有任务到达,创建核心线程;

​ 当任务到达6时,核心线程已满,将创建线程进入队列;

​ 当任务到达11时,队列已满,创建非核心线程;

​ 当任务到达16时,已超过最大线程数量与队列,执行拒绝策略.

关闭线程池:

​ shutdownNow: 对正在执行的任务进行interrupt()停止执行;未开始的全部取消,返回未开始的列表;

​ shutdown: 不会强行终止已提交或正在执行的任务,但也不会接受新的任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值