Java并发编程
文章平均质量分 81
并发编程实战
喜欢撸铁的程序员
这个作者很懒,什么都没留下…
展开
-
前置、JMM内存模型
由于线程对共享变量的操作都是线程拷贝到各自的工作内存进行操作后才写回到主内存中的,这就可能存在一个线程A修改了共享变量x的值,还未写回主内存时,另外一个线程B又对主内存中同一个共享变量x进行操作,但此时A线程工作内存中共享变量x对线程B来说并不可见,这种工作内存与主内存同步延迟现象就造成了可见性问题。:作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。:作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中。原创 2024-02-28 10:29:12 · 849 阅读 · 0 评论 -
十五、线程池
比如,现在我的电脑只有一个CPU,如果有两个线程在同时执行找素数的任务,那么这个CPU就需要额外的进行线程上下文切换,从而达到线程并行的效果,此时执行这两个任务的总时间为:任务执行时间*2+线程上下文切换的时间。以上只是理论,实际工作中情况会更复杂,比如一个应用中,可能有多个线程池,除开线程池中的线程可能还有很多其他线程,或者除开这个应用还是一些其他应用也在运行,所以实际工作中如果要确定线程数,最好是压测。答案是会的,那有没有可能核心线程数在执行任务时都出错了,导致所有核心线程都被移出了线程池?原创 2024-02-26 08:51:19 · 811 阅读 · 0 评论 -
十四、并发容器
💡答:可以把容器当做一个方法来理解,方法中存在共享变量,多个线程同时执行如果要保证这个方法执行的结果没有问题,要么在方法内部添加锁,要么让使用者在执行方法的外层加锁。显然前者更合适,这就是为什么容器要加锁的原因。java集合框架中主要分为四大类别:list、set、map、queue,其中arrayList、linkedList、hashset这些都是非线程安全的。原创 2024-02-26 08:50:48 · 879 阅读 · 0 评论 -
十三、读锁和写锁
读读场景下不会出现线程安全问题,读写、写写场景下会出现线程安全问题。在没有写操作的时候,多个线程去读不会产生问题,但是当有一个线程想要去写操作时,就不应该有其他读线程和写线程。读写锁顾名思义就是把一把锁分为读锁和写锁两部分,读锁允许多个线程共同获得,写锁只允许一个线程获得。💡 问题:读操作为什么要加锁?在读取数据的方法中如果要那这个数据做一些业务处理,此时没有加锁被其他线程给改变了,就会造成类似于“脏读”的现象。内部维护了两个API规范,读锁和写锁。线程进入读锁的条件:线程进入写锁的条件:读写锁有以下三个原创 2024-02-26 08:50:14 · 589 阅读 · 0 评论 -
十二、AQS原理
AbstractQueuedSynchronizer(抽象的同步等待队列)的简称。之前讲到的ReentrantLock、Semaphore、CountDownLatch都是基于AQS来实现的。可以理解为AQS是一个抽象模板,帮我们定义了一些列的规范和工具,只需要重写其抽象方法就可以自己实现一把锁。原创 2024-02-25 11:22:05 · 784 阅读 · 1 评论 -
十一、AQS之CountDownLatch
等待指定线程执行完毕之后才开始执行,这个类使一个线程等待其他线程都执行完毕之后再执行。使通过一个计数器来实现的,计数器的初始值是线程的数量,每当一个线程执行完毕之后,调用countDown方法,计算器值减1,当计数器的值为0时,表示所有线程都执行完毕了,等待的线程就可以恢复工作了。原创 2024-02-25 11:21:34 · 270 阅读 · 0 评论 -
十、AQS之Semaphore
信号量Semaphore(信号量)是一种用于多线程编程的同步工具,用于控制同时访问某个资源的线程数量。它与ReentrantLock和synchronized的区别是它不是锁,但是它和锁又有着相似的地方如:资源访问的排他性。Semaphore维护了一个计数器,线程可以通过调用acquire()方法来获取Semaphore中的许可证,当计数器为0时,调用acquire()的线程将被阻塞,直到有其他线程释放许可证;原创 2024-02-25 11:21:04 · 234 阅读 · 0 评论 -
九、AQS之ReentrantLock
获取等待通知组件,该组件和当前的锁绑定,当前线程只有获取了锁,才能调用该组件的await()方法,而调用后,当前线程将释放锁。[阻塞] 可中断的获取锁,和lock()方法不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程。当某个线程在等待锁时,如果不想一直等待我们可以给它设置超时时间,也可以通过外部来中断等待。案例:执行装车任务,一个车道同时只允许一个车辆执行,其他车辆超时未执行要打印错误报告。可重入锁又叫递归锁,同一个线程可以重复获取同一把锁,不会因为之前的锁没有释放而阻塞。原创 2024-02-25 11:20:29 · 297 阅读 · 0 评论 -
八、CAS 原子操作
共享变量原子操作是操作系统层面的的逻辑。原创 2024-02-25 11:19:53 · 459 阅读 · 0 评论 -
七、ThreadLocal
线程本地变量。原创 2024-02-25 11:19:23 · 410 阅读 · 0 评论 -
六、等待唤醒方式
notify:所在的synchronized代码块全部被执行完后才会释放锁,因为在执行该方法是通知方法,只有将条件全部修改完后才会通知其他线程,所以一般该方法都会在synchronized代码块最后执行。通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。调用该方法的线程进入 WAITING状态,只有等待另外线程的通知或被中断才会返回.需要注意,调用wait()方法后,会释放对象的锁。通知所有等待在该对象上的线程。原创 2024-02-25 11:18:42 · 405 阅读 · 0 评论 -
五、synchronized与volatile
但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作,包括数据之间的共享,协同处理事情。Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的。使用于一个线程写,多个线程读的场景,它能够保证每次读取都能够是最新的值,但是不能够保证读取到最新的值使用的过程中不被修改。原创 2024-02-25 11:18:05 · 226 阅读 · 0 评论 -
四、线程的生命周期
为什么用户线程又被称为协程呢?我们知道,内核线程的切换开销是来自于保护和恢复现场的成本, 那如果改为采用用户线程, 这部分开销就能够省略掉吗?答案还是“不能”。但是,一旦把保护、恢复现场及调度的工作从操作系统交到程序员手上,则可以通过很多手段来缩减这些开销。原创 2024-02-25 11:17:24 · 243 阅读 · 0 评论 -
三、线程的中断方式
不建议使用的原因主要有:以suspend()方法为例,在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。正因为suspend()、resume()和stop()方法带来的副作用,这些方法才被标注为不建议使用的过期方法。是否中断线程,如果当前线程是中断状态,会清除当前线程的中断标志,重新让当前线程状态为正常状态。原创 2024-02-25 11:16:47 · 163 阅读 · 0 评论 -
二、创建线程的方式
Thread类是Java里对线程概念的抽象,可以这样理解:我们通过new Thread()其实只是new出一个Thread的实例,还没有操作系统中真正的线程挂起钩来。从Thread的源码可以看到,Thread的start方法中调用了start0()方法,而start0()是个native方法,这就说明Thread#start一定和操作系统是密切相关的。start()方法让一个线程进入就绪队列等待分配cpu,分到cpu后才调用实现的run()方法,start()方法不能重复调用,如果重复调用会抛出异常(原创 2024-02-25 11:16:05 · 328 阅读 · 0 评论 -
一、并发编程基本概念
进程就可以视为程序的一个实例。显然,程序是死的、静态的,进程是活的、动态的。同一时刻,一个CPU核心只能运行一个线程,也就是CPU内核和同时运行的线程数是1:1的关系,也就是说8核CPU同时可以执行8个线程的代码。线程必须依赖于进程而存在,线程是进程中的一个实体,是CPU调度和分派的基本单位,它是比进程更小的、能独立运行的基本单位。这就意味着,操作系统要保证线程在调度前后的正常执行,所以,操作系统中就有上下文切换的概念,它是指CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。原创 2024-02-25 11:15:09 · 509 阅读 · 0 评论