一、java锁的类型
创新互联公司是一家集网站建设,平桂企业网站建设,平桂品牌网站建设,网站定制,平桂网站建设报价,网络营销,网络优化,平桂网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
java的锁有这么几类。
乐观锁和悲观锁
可重入和不可重入
公平锁和非公平锁
排他锁和共享锁
二、synchronized
synchronized基本原理是通过CPU指令实现的。在jdk1.6之前是很重的锁。因为java的多线程与操作系统的线程是一一对应的。当java线程阻塞的时候需要切换到内核态的线程进行阻塞,唤醒的时候又要从内核态切换到用户态,进行了很重的上下文切换。那么能不能当一个线程获取不到锁的时候不阻塞呢?自旋可以吗?这样就有了synchronized的四种实现:无锁、偏向锁、轻量锁、重量锁。
synchronized锁的是java的对象头,再详细点是mark word。
无锁
这个没有什么好说的。没有将这个对象通过synchronized包括。
偏向锁
当只有一个线程在访问锁的时候,会在mark word中通过CAS的方式设置当前线程的threadId。如果成功的话,加锁成功(由于只有一个线程,肯定成功)。这样当这个线程再次请求锁的时候,看mark word的thread id和自己是否相同,如果相同加锁成功。注意,它是没有解锁操作的。如果是另一个线程也来了,由于上一个线程没有解锁操作,这个新线程的CAS肯定失败。这时当JVM没有字节码要执行的时候(全局安全点),会检查上一个线程有没有结束,如果结束,则通过CAS将mark word中的thread id字段更新为新线程的threadId。如果上一个线程没有结束,这就存在并发了。偏向锁无法完成使命,需要升级为轻量锁。
轻量锁
接着上面偏向锁的上一个线程A和新的线程B的例子。JVM此时进行一下线程A对mark word的操作。将mark word拷贝到当前线程的栈空间中,CAS操作mark word的指针指向这个栈空间的地址,CAS操作当前线程的栈空间再加一个指向mark word的指针,这两个操作成功后,其实第一个CAS成功就是成功,这样线程A就获得了锁,升级成为了轻量锁。线程B会自旋等待线程A的释放。线程A怎么释放锁呢?只要将第一个CAS操作的指针(mark word指向线程栈的指针)释放了就可以了,线程B自旋检测mark word的指向,去抢占锁。如果此时又来一个线程C呢?是不是也自旋?可以同时有几个线程自旋?线程B能自旋多少次?这些都是有JVM参数可配置的。
重量锁
这个其实也没什么好说的。存在并发访问时,直接将线程切换到内核态阻塞。
三、ReentrantLock
ReentrantLock是通过AQS(AbstractQueuedSynchronizer)实现的。需要解决的问题:
需要有个状态表示这个lock对象是不是被抢占了,如果可重入的话,被这个线程抢占了多少次。这个状态标识其实就是AQS的state成员变量。对state的操作肯定要线程安全。可以通过CAS解决。
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- // 这个是公平锁的实现。需要判断队列中有没有等待的线程,
- // 如果没有才进行CAS抢占
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- // 这里就是可重入逻辑
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
多个线程同时抢占lock,只有一个线程能成功,其他线程怎么排队呢?排队的线程怎么抢占锁呢?这就用到了一个队列。这个队列的插入是通过自旋和CAS实现的。
- private Node addWaiter(Node mode) {
- Node node = new Node(mode);
- // 循环尝试
- for (;;) {
- Node oldTail = tail;
- if (oldTail != null) {
- // 无锁修改前驱指针
- node.setPrevRelaxed(oldTail);
- // CAS修改tail
- if (compareAndSetTail(oldTail, node)) {
- // 修改后续指针
- oldTail.next = node;
- return node;
- }
- } else {
- initializeSyncQueue();
- }
- }
- }
排队的线程抢占lock呢?
- final boolean acquireQueued(final Node node, int arg) {
- boolean interrupted = false;
- try {
- for (;;) {
- final Node p = node.predecessor();
- // 如果前驱节点是头节点,并且获取锁成功,直接返回。
- // 但是大多数情况,可能运气没这么好
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- return interrupted;
- }
- // 是否需要阻塞
- if (shouldParkAfterFailedAcquire(p, node))
- // 这里阻塞
- interrupted |= parkAndCheckInterrupt();
- }
- } catch (Throwable t) {
- cancelAcquire(node);
- if (interrupted)
- selfInterrupt();
- throw t;
- }
- }
怎么唤醒上面阻塞的线程呢?这就要看下释放逻辑。
- public final boolean release(int arg) {
- // 释放lock
- if (tryRelease(arg)) {
- Node h = head;
- if (h != null && h.waitStatus != 0)
- unparkSuccessor(h); // 唤醒头结点的后续节点。注意头结点是虚节点,没有实在意义
- return true;
- }
- return false;
- }
【编辑推荐】
分享题目:查漏补缺synchronized和ReentrantLock的基本原理
网站路径:http://www.shufengxianlan.com/qtweb/news24/67424.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联