多线程
文章平均质量分 87
java多线程学习/面试指南
拾光师
瞧一瞧,看一看,好吃还不贵。量大管饱
多年java开发经验,现征战于互联网广告行业
分享各种java相关知识,每周至少五更
展开
-
并发容器之BlockingQueue阻塞队列
生产者生产的数据直接会被消费者获取并消费,只会在没有可消费的数据时,阻塞数据的消费者,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间,newCachedThreadPool线程池使用了该队列。BlockingQueue接口是在jdk5版本提供的,在线程池中用到了阻塞队列来实现,阻塞队列是深入学习线程池的基础,该队列通常是有限的容量,如果队列已满添加操作就会阻塞,如果队列为空,移除操作就会阻塞。,创建时需要指定容量,实现了FIFO(先进先出)排序机制。原创 2024-03-26 12:11:44 · 881 阅读 · 0 评论 -
并发容器之ConcurrentSkipListMap
SkipList让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过以时间换空间,在每个节点中增加了向前的指针,在插入、删除、查找时可以忽略一些不可能涉及到的节点,从而提高效率。每一层都是一个有序的链表,默认是升序,也可以根据创建映射时所提供的Comparator进行排序,具体取决于使用的构造方法。如果一个元素出现在Level i 的链表中,则它在Level i 之下的链表也都会出现。每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。原创 2024-03-27 10:16:50 · 380 阅读 · 0 评论 -
并发容器之ConcurrentMap
哈希函数根据hashCode计算出哈希值,这里的hash值与HashMap的计算方式稍微有点不同,在低十六位异或高十六位之后还需要与HASH_BITS在进行与运算,HASH_BITS的值是0x7fffffff,转为二进制是31个1,进行与运算是为了保证得到的hash值为正数。CAS包含三个操作数,---内存中的值(V),预期原值(A),新值(B) 如果内存中的值和A的值一样,就可以将内存中的值更新为B。CAS是compare and swap的缩写,即我们所说的比较交换,CAS属于乐观锁。原创 2024-03-25 10:01:38 · 692 阅读 · 0 评论 -
并发容器之CopyOnWrite
CopyOnWriteArrayList是同步List的并发替代品,是java并发包java.util.concurrent中提供的用于并发操作且线程安全的ArrayList,可以提供更好的并发性,并且避免了在迭代期间对容器加锁和复制,在每次修改的时,会创建一个新的容器拷贝,以此来实现可变性。当写共享数据时,将旧数据复制出来一份作为新数据,只修改新数据,修改完新数据后将新数据的引用赋值给原来数据的引用,在写数据的过程中,所有读取共享数据都是读的旧数据。当读取共享数据时,直接读取,不需要有其他操作。原创 2024-03-24 13:56:25 · 837 阅读 · 0 评论 -
并发容器之ConcurrentLinkedQueue
head 和 tail 的更新总是间隔了一个,是为了减少CAS的更新操作,如果大量的入队操作,每次都要执行 CAS 进行 tail 的更新,汇总起来对性能也会是大大的损耗。head 更新时机:并不是每次出队时都更新 head 节点,当 head 节点里有元素时,直接弹出 head 节点里的元素,而不会更新 head 节点;tail 更新时机:tail 节点不总是尾节点,如果 tail 节点的 next 节点不为空,则将入队节点设置成 tail 节点;如果队列中存在该元素,则删除该元素。原创 2024-03-23 14:03:30 · 514 阅读 · 0 评论 -
什么是CAS操作
之前说在java.util.concurrent.atomic包下提供的原子操作类底层使用的是CAS,那么什么是CAS呢,CAS的全称为Compare And Swap,比较并替换,CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。解决这个问题很简单,ABA的本质就是无法感知中间过程,那么加一个版本号就可以了,每次版本号递增1,在比较时,不仅要比较内存地址V和旧的预期值A,还要比较一下版本号。虽然CAS采用的无锁操作来提供性能,但是CAS并不是完美的,存在了很多的不足。原创 2024-03-21 10:36:02 · 772 阅读 · 0 评论 -
java内存模型JMM?
线程A和线程B之间交互,有一个共享变量x,在一开始时本地内存A、本地内存B、主内存中x的值都是0,线程A执行后,将x的值置为1,所以线程A的本地内存A中x的值变为1,当线程A和B进行通信时,线程A先将自己本地内存A中的x刷新到主内存中,此时主内存中的x变为了1,随后,线程B从主内存中读取x的值,此时线程B的本地内存B也变成了1。(线程操作共享变量时,先从主内存复制共享变量到自己的工作内存,然后对工作内存里的变量进行处理,处理完之后将变量值更新到主内存),不可以直接操作主内存的变量。LoadStore;原创 2024-03-19 10:18:11 · 808 阅读 · 0 评论 -
多线程的异常处理机制
在线程中如果出现异常想要额外进行一些操作的话,可以使用线程的异常处理机制,UncaughtExceptionHandler,这是线程的一个子接口,当一个未捕获的异常导致线程中断的时候JVM会使用thread.getUncaughtExceptionHandler()来查询线程的uncaughtExceptionHandler并将线程和异常作为参数传递给uncaughtException方法。方式二:继承ThreadPoolExecutor来重写afterExecute方法,处理异常。自定义Handler。原创 2024-03-18 09:48:27 · 944 阅读 · 0 评论 -
彻底理解线程池
ForkJoinPool用来通过分治技术将大任务拆分成小任务,再对每个小任务得到的结果进行汇总,在一个任务中,先检查将要执行的任务的大小,如果大于一个设定的大小,则将任务拆分成可以通过框架来执行的小任务,如果任务的大小比设定的要小,就可以直接执行,然后根据需要返回的任务的结果,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。的线程池,只有唯一的工作线程,所有的任务都是串行执行的,如果这个唯一的线程因为异常结束,会有新的线程来代替它,此线程保证所有任务的执行顺序按照任务的提交顺序执行。原创 2024-03-17 11:28:20 · 1293 阅读 · 0 评论 -
JDK提供的几种同步方式
,在完成一组正在其他线程中执行的操作之前,允许一个或多个线程一直等待,内部提供了一个计数器count,判断count计数不为0时则调用了await()的线程呈wait状态,也就是在屏障处等待,提供了countDown方法使得计数器减一,直到计数器为0表示条件成熟,此时所有调用await方法而阻塞的线程都会被唤醒。当一个线程到达指定的点后,调用await()方法等待其他线程直到其他所有线程到达,当最后一个线程调用await()方法时,CyclicBarrier对象将唤醒所有在等待的线程,继续向后执行。原创 2024-03-15 10:08:38 · 1056 阅读 · 0 评论 -
你还不知道ThreadLocal线程本地存储吗
那么有没有别的方法来解决呢?该类提供了线程局部变量,为每一个线程创建一个单独的变量副本,使得每个线程都可以独立的改变自己所拥有的变量副本,而不会影响其他线程所对应的副本,消除了竞争条件。如果一个线程是从其他某个线程中创建的,这个类将提供继承的值,如果一个线程A在线程局部变量中已有值,那么当线程A创建其他某个线程B时,线程B的线程局部变量将跟线程A是一样的,可以重写childValue()方法,该方法用来初始化子线程在线程局部变量中的值,使用父线程在线程局部变量中的值作为传入参数。原创 2024-03-14 10:20:07 · 571 阅读 · 0 评论 -
JDK中提供的Lock
ReentrantLock为Lock的实现类,实现了标准的互斥锁,底层使用AQS实现线程同步,使用CAS,比synchronized效率略高,一次最多只有一个线程持有ReentrantLock ---> 有公平锁和非公平锁,是可重入锁,每次重入会把拥有数++由于synchronized在很多情况下是不可控的,所以在jdk5出现了一个新的加锁方式Lock,提供了无条件的,可轮询的,可定时的,可中断的所获取操作,所有加锁和解锁都是显式的。Lock使用代码实现CAS,为API层面上的互斥锁;原创 2024-03-13 10:30:40 · 936 阅读 · 0 评论 -
让人头疼的AbstractQueuedSynchronizer究竟是什么?
FIFO双向链表,通过head节点和tail节点来记录队首和队尾元素,主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,可重写的方法tryAcquire(int arg)、tryRelease(int arg)、tryAcquireShared(int arg)、tryReleaseShared(int arg)、isHeldExclusively(),很多同步类都继承该类来实现同步逻辑,子类通过继承AQS并实现它的抽象方法来管理同步状态。,是一个抽象类,它是从java5开始的。原创 2024-03-12 10:09:50 · 688 阅读 · 0 评论 -
多线程如何进行线程通信
来通知那些等待该对象锁的其他线程,(如果有多个线程等待,notify会任意挑选一个线程来进行通知,notifyAll会对所有该对象上由于调用wait方法而被挂起的线程进行通知,),唤醒之后的线程会进入该对象的锁池中,需要竞争到对象的监视器锁才可以继续执行,调用notify()方法和notifyAll()方法之前,线程必须要获得该对象的对象级别锁,即只能在同步块或者同步方法中使用。使用lock.lock()来进行加锁,使用lock.unlock()方法来释放锁。下面来使用两种方式来分别展示一下线程间通信。原创 2024-03-11 10:18:38 · 429 阅读 · 0 评论 -
volatile可以保证原子性吗
cpu在执行代码的时候,为了减少变量访问的时间消耗可能会将代码中访问的变量值缓存到该CPU的缓存区,所以在相应代码再次访问某个变量时,读到的值可能来自于缓存区而不是主存,同样,代码对这些缓存过的变量的值的修改也可能只是被写入到缓存区,而没有回写到主存,且每个CPU都有自己的缓存区,因此一个CPU缓存区中的内容对于其他CPU是不可见的。StoreLoad屏障:对于Store1;LoadStore;内存可见性,volatile修饰的变量不会被缓存在寄存器中,每次都是从主存中读取,对于其他线程全部可见。原创 2024-03-10 14:52:47 · 1461 阅读 · 0 评论 -
一文带你彻底搞懂synchronized关键字
在执行monitorexit指令时,会将锁计数器-1,当计数器为0时,锁被释放。当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能,同步块执行速度非常快,竞争的线程不会阻塞,但是如果始终得不到竞争的线程,使用自旋会消耗CPU。当线程A执行完同步块之后,线程B获取对象锁,检查Mark word是否为偏向锁,且线程id是否为线程A,此时线程id变了,所以有不同的线程来获取锁对象,偏向锁升级为轻量级锁,并由线程B获取该锁。原创 2024-03-09 19:09:34 · 901 阅读 · 0 评论 -
线程使用Runnable还是Callable
CompletionService的功能是以异步的方式一边生产新的任务,一边处理已完成任务的结果,这样可以将执行任务与处理任务分离开进行处理,使用submit执行任务,使用take取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果,把线程池Executor和阻塞队列BlockingQueue融合在一起。Callable与Runnable不同,提供的方法为call()方法,大家看到,该方法是有返回值的,且可以抛出异常。看到该方法返回的是一个Future对象,Future对象是什么呢?原创 2024-03-08 10:17:50 · 441 阅读 · 1 评论 -
java线程简介
interrupted()方法,静态方法,检查线程是否被中断,与isInterrupted()不同的是,该方法如果发现当前线程被中断,则会清除中断标志,将中断状态重置为false,且该方法是静态方法,判断的是当前调用线程的中断标志而不是调用该方法的实例对象的中断标志。一般情况下使用多线程的时候,使用的线程个数会大于CPU个数,但是每个CPU同一时刻只能被一个线程使用,CPU资源采用了时间片轮转的策略为每个线程分配一个时间片,当前线程的时间片到后,会处于就绪状态让出CPU,这就是。原创 2024-03-07 10:26:36 · 917 阅读 · 0 评论