![](https://img-blog.csdnimg.cn/20201014180756927.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
并发
文章平均质量分 83
龙贝子
软件工程师/项目管理
展开
-
并发(13)
java.util.concurrent包里的BlockingDeque接口表示一个线程放入和提取实例的双端队列。BlockingDeque类是一个双端队列,在不能插入元素时,他将阻塞住试图插入元素的线程;在不能抽取元素时,他将阻塞住试图抽取的线程。deque(双端队列)是”Double Ended Queue“的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列。在线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到BlockingDeque。原创 2024-01-08 19:25:43 · 967 阅读 · 1 评论 -
并发(12)
BlockingQueue通常用于一个线程生产对象,而另一个线程消费这些对象的场景。下图是对这个原理的阐述:一个线程往里边放,另外一个线程从里面取的一个BlockingQueue。一个线程将会持续生产新对象并将其插入到队列之中,直到队列达到他所能容纳的临界点。也就是说,他是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会在往里面插入新对象时发生阻塞。他会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。负责消费的线程将会一直从该阻塞队列中拿出对象。原创 2024-01-08 16:47:50 · 937 阅读 · 0 评论 -
并发(11)
而树化的临界值选择8是通过泊松分布算出的,结点个数为8出现的几率是亿分之6。原创 2024-01-08 13:46:15 · 417 阅读 · 0 评论 -
并发(10)
ReentrantReadWriteLock为什么不支持锁升级?ReentrantReadWriteLock不支持锁升级(把持读锁,获取写锁,最后释放读锁的过程)。目的也是保证数据可见性,如果读锁已经被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。原创 2024-01-08 13:02:29 · 948 阅读 · 0 评论 -
并发(9)
AQS定义了两种资源获取方式:独占(只有一个线程能独立访问执行,又根据是否按队列的顺序分为公平锁和非公平锁,如ReentrantLock)共享(多个线程可同时访问执行,如Semaphore,CountDownLatch,CyclicBarrier)。ReentrantReadWriteLock可以看成是组合式,允许多个线程同时对某一资源进行读。可重入:(来源于维基百科)若一个程序或子程序可以”在任意时刻中断然后操作系统调度执行另外一段代码,这点代码又调用了该子程序不会出错“,则成其为可重入的。原创 2024-01-08 12:04:37 · 866 阅读 · 0 评论 -
并发(8)
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效的构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。AQS核心思想是,如果请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。原创 2024-01-07 16:54:49 · 881 阅读 · 0 评论 -
并发(7)
说明:本程序先执行park,然后再执行unpark,进行同步,并且在unpark的前后都调用了getBlocker,可以看到两次的结果不一样,并且第二次调用的结果为null,这是因为在调用unpark之后,执行了Lock.park(Object blocker)函数中的setBlocker(t,null)函数,所以第二次调用getBlocker时为Null。使用wait/notify实现同步时,必须先调用wait,后调用notify,如果先调用notify,再调用wait,将起不了作用。原创 2024-01-07 16:02:49 · 360 阅读 · 0 评论 -
并发(6)
CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进性比较两个值相等,然后原子地更新某个位置的值,经过调查发现,其实现方式是基于硬件平台的汇编指令,就是说CAS是靠硬件实现的,JVM只是封装了汇编调用,那些AtomicInteger类便是使用了这些封装后的接口。简单解释:CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下载旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。原创 2024-01-07 15:38:16 · 812 阅读 · 0 评论 -
并发(6)
StampedLock控制锁有三种模式(写,读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,他用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择另一个线程进入,这只是一种逻辑上的理解。原创 2024-01-07 14:45:16 · 773 阅读 · 0 评论 -
并发(5)
在一个线程中,初次读对象引用和初次读该对象包含的final域,JMM会禁止这两个操作的重排序。(注意,这个规则仅仅是针对处理器),处理器会在读final域操作的前面插入一个LocalLoad屏障。实际上,读对象的引用和读该对象的final域存在间接依赖性,一般处理器不会重排序这两个操作。PS:很有意思的是,如果以X86处理为例,X86不会写-写重排序,所以StoreStore屏障可以省略。由于不会对有间接依赖性的操作重排序,所以在X86处理器中,读final域需要LoadLoad屏障也会被省略掉。原创 2024-01-07 12:45:17 · 353 阅读 · 0 评论 -
并发(4)
Base和Son都有方法test(),但是这并不是一种覆盖,因为private所修饰的方法是隐式地final,也及时无法被继承,所以更不用说是覆盖了,在Son中test()方法不过是属于Son的新成员罢了,Son进行向上转型得到father,但是father.test()是不可执行的,因为Base中的test()方法是private的,无法被访问到。synchronized实际上是非公平的,新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,这样有利于提高性能,但是也可能导致饥饿现象。原创 2024-01-06 21:09:59 · 948 阅读 · 0 评论 -
并发(3)
monitorexit指令:释放对于monitor的所有权,释放过程很简单,就是讲monitor的计数器减1,如果减完以后,计数器不是0,则代表刚才是重入进来的,当前线程还继续持有这把锁的所有权,如果计数器变成0,则代表当前线程不再拥有该monitor的所有权,即释放锁。该图可以看出,任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态明显呈状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得到线程就会有机会重新获取该监视器。原创 2024-01-06 20:18:39 · 821 阅读 · 0 评论 -
并发(2)
对于以下代码,在main()中启动一个线程之后再中断他,由于线程中调用了Thread.sleep()方法,因此会抛出InterruptedException,从而提前结束线程,不执行之后的语句。如果一个线程的run()方法执行一盒无限循环,并且没有执行sleep()等会抛出InterruptedException的操作,那么调用线程的interrupt()方法就无法使线程提前结束。synchronized中的锁是非公平的,ReentrantLock默认情况下也是非公平的,但是也可以是公平的。原创 2024-01-06 17:42:10 · 941 阅读 · 0 评论 -
并发(1)
随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其他线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。阻塞和等待的区别在于,阻塞是被动的,他是等待获取一个排他锁。在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。原创 2024-01-06 16:31:36 · 853 阅读 · 0 评论