2025年并发编程和J.U.C并发包

并发编程和J.U.C并发包1 J U C 并发包概述 1 j u c 是 JDK 的核心工具包 是 JDK1 5 之后 由 Doug Lea 撰写 实现并引入 整个 java util concurrent 包 按照功能可以大致划分如下 juc locks 锁框架 juc atomic 原子类框架

大家好,我是讯享网,很高兴认识大家。

1、J.U.C并发包概述

(1)j.u.c是JDK的核心工具包,是JDK1.5之后,由 Doug Lea(撰写)实现并引入。

整个java.util.concurrent包,按照功能可以大致划分如下:

  • juc-locks 锁框架
  • juc-atomic 原子类框架
  • juc-sync 同步器框架、工具类
  • juc-collections 集合框架
  • juc-executors 执行器框架

2、并发编程

(1)死锁问题

什么是死锁:

多线程竞争共享资源,导致线程相互等待,程序无法向下执行。

代码演示:

public class DeadLock { 
    // 定义两个对象 private static Object objA = new Object(); private static Object objB = new Object(); public static void main(String[] args) { 
    // 线程一 Thread t1 = new Thread(()->{ 
    synchronized (objA){ 
    try { 
    Thread.sleep(1000); // 线程等待 } catch (InterruptedException e) { 
    e.printStackTrace(); } System.out.println("AAAAAAA"); synchronized (objB){ 
    System.out.println("BBBBBBB"); } } }); // 线程二 Thread t2 = new Thread(()->{ 
    synchronized (objB){ 
    System.out.println("CCCCCCC"); synchronized (objA){ 
    System.out.println("DDDDDDD"); } } }); // 启动线程 t1.start(); t2.start(); } // synchronized锁:一定要等待锁释放,别的线程才可以进行下一步操作 } 

讯享网

cmd命令查看线程执行情况:

  • jps命令:查看java程序进程id信息
  • jstack命令+进程ID:查看指定进程堆栈信息

避免死锁:

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock .tryLock (timeout )来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况

mysql锁表语句:

lock锁:
在这里插入图片描述
讯享网
行级锁/排他锁 [使用FOR UPDATE]

  • 启动事务,BEGIN;
  • 锁定某一行 where条件需要主键,SELECT * FROM products WHERE id=1 FOR UPDATE
  • 事务提交[或者 ROLLBACK 事务回滚],COMMIT

在启动事务然后锁定某一行后,在事务提交之前或者事务回滚之前该行会一直处于锁定状态,此时只允许当前线程进行读写操作,其他线程只能进行读操作,其他写操作全部被堵塞…

读:可以并发
写:不可并发操作

小结:

1、什么是死锁:

  • 多线程竞争共享资源,导致线程相互等待,程序无法向下执行

2、死锁产生的条件:

  • A.有多个线程
  • B.有多把锁
  • C.有同步代码块嵌套

3、如何避免死锁:

  • 干掉其死锁产生的条件中一个条件即可
(2)上下文过度切换

介绍上下文切换:

大概意思:CPU来回切换执行进程中的单个线程,切换速度太快了,给人的感觉就是同时在执行。

  • 单核处理器也支持多线程执行代码,CPU 通过给每个线程分配CPU 时间片来实现这个机制。
  • 时间片:CPU 分配给各个线程的时间,因为时间片非常短,所以CPU 通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。
  • CPU 通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。

举例介绍:

这就像我们同时读两本书,当我们在读一本英文的技术书时,发现某个单词不认识,于是便打开中英文字典,但是在放下英文技术书之前,大脑必须先记住这本书读到了多少页的第多少行,等查完单词之后,能够继续读这本书。这样的切换是会影响读书效率的,同样上下文切换也会影响多线程的执行速度。

多线程的执行速度:

代码演示:

讯享网public class ConcurrencyTest { 
    // 定义总次数 private static final long count = ; public static void main(String[] args) throws InterruptedException { 
    concurrency(); serial(); } // 并发执行函数 private static void concurrency() throws InterruptedException { 
    // 定义开始时间 long start = System.currentTimeMillis(); // 定义线程 Thread thread = new Thread(new Runnable() { 
    @Override public void run() { 
    int a = 0; for (long i = 0; i < count; i++) { 
    a += 5; } } }); thread.start(); // 启动线程 int b = 0; for (long i = 0; i < count; i++) { 
    b--; } // 计算执行完毕的时间 long time = System.currentTimeMillis() - start; thread.join(); // 线程阻塞将继续向下执行 // 打印时间 System.out.println("concurrency :" + time + "ms,b=" + b); } // 单线程执行函数 private static void serial() { 
    // 定义开始时间 long start = System.currentTimeMillis(); int a = 0; for (long i = 0; i < count; i++) { 
    a += 5; } int b = 0; for (long i = 0; i < count; i++) { 
    b--; } // 计算时间差,并打印结果 long time = System.currentTimeMillis() - start; System.out.println("serial:" + time + "ms,b=" + b + ",a=" + a); } } 

测试结果:

  • 当并发执行累加操作不超过百万次时,速度会比单线程执行累加操作要慢。
  • 并发执行的速度会比串行慢,是因为线程有创建和上下文切换的开销。
  • 数据量较大,多线程优于单线程。

减少上下文切换:

  • 无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash 算法取模分段,不同的线程处理不同段的数据。
  • CAS算法:Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
  • 使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
小讯
上一篇 2025-03-19 19:15
下一篇 2025-02-13 22:52

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/118197.html