1.java锁的种类java锁有几种类型。乐观锁和悲观锁乐观锁是指JVM认为不加锁也能保证并发的正确性。典型的实现是AtomicInteger等实现。悲观锁需要加锁和互斥。典型的实现有Synchronized(Synchronized是乐观锁还是悲观锁其实和具体实现有关,大部分场景都是悲观锁)和ReentrantLock。可重入与不可重入可重入是指当一个线程获取了锁但没有释放锁时,如果线程想再次获取锁,仍然可以成功获取到锁。Synchronized和ReentrantLock都是可重入锁。Non-reentrant是一个可重入的否定命题,这样你自己就会陷入僵局。不应该有这样的实现。公平锁和非公平锁公平锁是指先请求锁的线程必须先获得锁,即FIFO。说合理就公平吗?可能不一定,因为它会引起上下文切换。ReentrantLock默认是非公平锁,但是可以通过构造函数构造公平锁实例。非公平锁是指新线程有机会先获得锁,也就是可以插队。合理吗?可能不合理,因为可能会造成“饥饿”现象:队列中的老线程始终无法拿到锁。synschronized其实就是一个非公平锁。独占锁和共享锁独占锁是指一个线程获得锁后,其他线程就不能再获得锁。在大多数情况下,它是一个独占锁。共享锁是指多个线程可以同时获取锁。多个线程可以同时获取读锁是很常见的。二、synchronized的基本原理是通过CPU指令实现的。jdk1.6之前是重锁。因为java的多线程与操作系统的线程是一一对应的。java线程阻塞时需要切换到内核态的线程进行阻塞,唤醒时需要从内核态切换到用户态,进行重上下文切换。那么一个线程在获取不到锁的时候能不能不被阻塞呢?旋转可能吗?这样一来,synchronized就有四种实现方式:无锁、偏向锁、轻量级锁、重度锁。synchronized锁是java的对象头,更详细点是markword。无锁没什么好说的。该对象不通过同步包含。偏向锁当只有一个线程在访问锁时,会通过CAS在markword中设置当前线程的threadId。如果成功,则加锁成功(因为只有一个线程,所以一定是成功的)。这样当线程再次请求锁时,检查markword的threadid是否和自己相同,如果相同则锁成功。请注意,它未解锁。如果另一个线程也来了,由于前一个线程没有解锁,这个新线程的CAS肯定失败了。这时候,当JVM没有字节码可以执行的时候(全局安全点),它会检查之前的线程是否已经结束。如果结束,markword中的threadid字段会通过CAS更新为新线程的threadId。如果前面的线程还没有结束,就存在并发。偏向锁无法完成其使命,需要升级为轻量级锁。轻量级锁沿用了上面的例子,之前的线程A和新的线程B都偏向于锁。这时JVM执行线程A对markword的操作。将markword复制到当前线程的栈空间,CAS操作markword的指针指向这个栈空间的地址,将markword的指针添加到当前线程的栈空间进行CAS操作.这两个操作成功后,第一个A成功的CAS就成功了,这样线程A就获得了锁,升级为轻量级锁。线程B将自旋并等待线程A被释放。线程A如何释放锁?只要第一次CAS操作的指针(指向线程栈的markword的指针)被释放,线程B就会自旋检测markword的指针从而抢占锁。如果此时有另一个线程C怎么办?它也旋转吗?有多少个线程可以同时旋转?线程B可以旋转多少次?这些都可以通过JVM参数进行配置。重量锁没什么好说的。有并发访问时,直接把线程切换到内核态阻塞。3、ReentrantLockReentrantLock是通过AQS(AbstractQueuedSynchronizer)实现的。待解决的问题:需要有一个状态表示锁对象是否被抢占,如果是可重入,则被本线程抢占了多少次。这个状态标志其实就是AQS的状态成员变量。对状态的操作必须是线程安全的。可以通过CAS解决。protectedfinalbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();intc=getState();if(c==0){//这是公平锁的实现。需要判断队列中是否有等待线程,//如果没有则进行CAS抢占if(!hasQueuedPredecessors()&&compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}//这里是可重入逻辑elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)thrownewError("Maximumlockcountexceeded");setState(nextc);returntrue;}returnfalse;}多线程抢占锁在同一时间只有一个线程可以Success,其他线程如何排队?排队线程如何抢占锁?这使用队列。这个队列的插入是通过自旋和CAS实现的。privateNodeaddWaiter(Nodemode){Nodenode=newNode(mode);//循环tryfor(;;){NodeoldTail=tail;if(oldTail!=null){//修改无锁前驱指针node.setPrevRelaxed(oldTail);//CASmodifytailif(compareAndSetTail(oldTail,node)){//修改后续指针oldTail.next=node;returnnode;}}else{initializeSyncQueue();}}}排队线程抢锁怎么办?finalbooleanacquireQueued(finalNodenode,intarg){booleaninterrupted=false;try{for(;;){finalNodep=node.predecessor();//如果前驱节点为头节点且获取锁成功,则直接返回。//但在大多数情况下,你可能就没那么幸运了(p,node))//blockinterrupted|=parkAndCheckInterrupt();}}catch(Throwablet){cancelAcquire(node);if(interrupted)selfInterrupt();throwt;}}如何唤醒上面阻塞的线程?这取决于下一个发布逻辑。publicfinalbooleanrelease(intarg){//释放lockif(tryRelease(arg)){Nodeh=head;if(h!=null&&h.waitStatus!=0)unparkSuccessor(h);//唤醒头节点的后续节点。注意头节点是一个虚拟节点,没有实际意义returntrue;}returnfalse;}【小编推荐】Windows10这个功能已经被禁用了!教你如何彻底关闭C++,C++程序员谁先完蛋?2021年值得关注的人工智能趋势RAID磁盘阵列适合你吗?在一篇文章中了解Windows10是绝唱!微软新系统开始更改版本号
