多线程线程的状态。1、线程池提交任务有4种情况:小于corePoolSizeaddWorker()。大于corePoolSize的workQueue.offer(command)直接增加任务,增加失败则拒绝。拒绝策略AbortPolicy抛出异常,默认。CallerRunsPolicy不使用线程池执行。DiscardPolicy直接丢弃。DiscardOldestPolicy丢弃队列中最早的任务。2.lockSychronized的原理和用法:在JDK1.6之前,synchronized只有方法代码块中有传统的锁机制,所以给开发者留下的印象是synchronized关键字相比其他同步机制表现不佳。在JDK1.6中,引入了两种新的锁机制:偏向锁和轻量级锁。它们的引入是为了解决在没有多线程竞争或者几乎没有竞争的场景下使用传统锁机制带来的性能开销。锁升级:对象头中存在偏向锁->轻量级锁->轻量级锁的映射关系。32位系统上的状态如图:偏向锁:当JVM启用偏向锁时,那么新创建的对象都是偏向的。此时markword中的threadid为0,表示没有线程偏向锁过程:对象第一次被线程锁时,发现是无偏的,则将threadid改为当前线程id,成功继续执行同步块中的代码,失败则升级为轻量级锁,当线程再次进入同步块时,发现当前线程是偏向的通过锁,并在通过一些额外的检查后继续执行。当其他线程进入同步块,发现有偏向线程时,就会进入取消偏向锁的逻辑。解锁过程:栈中最新的锁记录的obj字段设置为null轻量级锁:在线程执行同步块之前,JVM会在线程的栈帧上创建一个LockRecord。它包括一个DisplacedMarkWord,该MarkWord存储对象头中的markword和一个对象头指针。加锁过程:在线程栈中创建一个LockRecord,将其obj引用域指向锁对象。通过CAS指令将LockRecord地址放入对象头的markword中。如果对象处于无锁状态,则修改成功,即获得了轻量级锁。如果失败,则转到第3步。如果线程仍然持有锁,则说明这是一次锁重入。将LockRecord(DisplacedMarkWord)的第一部分设置为null,作为重入计数器。然后最后走到这一步,也就是有竞争,展开成权重锁。解锁过程:遍历线程栈,找到所有obj字段等于当前锁对象的LockRecords。如果LockRecord的DisplacedMarkWord为null,则表示重入。将obj设置为null,然后继续。如果LockRecord的DisplacedMarkWord不为空,则使用CAS命令将对象头的markword恢复为DisplacedMarkWord。如果成功则继续,否则扩展为重量级锁重量级锁:使用JVM监视器(Monitor)java会为每个object对象分配一个监视器,当一个对象的同步方法(synchronizedmethods)有多个时被调用线程,对象的监视器将负责处理这些访问的并发独占请求。在代码块上修饰Sychronized时,会用到monitorenter指令和monitorexit指令。monitorenter的过程是这样的:如果Monitor中的entry数为0,则线程进入Monitor,然后entry数+1,则线程成为Monitor的owner。如果线程被占用,线程会一直阻塞,直到monitorentry数为0。尝试获取monitorexit的过程如下:指令执行时,monitor的条目数减1.如果条目数为0,则线程将退出监视器。其他被阻塞的线程可以在试图获得这个Monitor的所有权时,Synchronize在作用于该方法时会添加一个ACC_SYNCHRONIZED标志。当有这个标志时,线程执行会先获取Monitor,获取成功才能执行方法体。3.AQS//获取资源占有的acquire方法publicfinalvoidacquire(intarg){/**尝试获取,tryAcquire方法是子类必须实现的方法,*比如公平锁和非公平锁的区别在于在tryAcquire方法中有不同的实现。*如果获取失败,addWaiter方法将node节点包裹起来,放入node双向链表中。然后acquireQueued阻塞线程,循环获取资源所有权。*/if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}protectedbooleantryAcquire(intarg){thrownewUnsupportedOperationException();}privateNodeaddWaiter(Nodemode){//新建node节点,waitStatus初始值为0Nodenode=newNode(Thread.currentThread(),mode);//尝试enq的快速路径;在节点pred=tail发生故障时备份到完整enq;//如果尾部不为空,说明之前已经初始化过节点双向链表,则直接将新的节点节点添加到尾部if(pred!=null){node.prev=pred;if(compareAndSetTail(pred,node)){pred.next=node;返回节点;}}//如果tail为null,说明节点双向链表之前没有初始化过,则调用enq方法初始化节点双向链表,将新节点添加到尾部enq(node);返回节点;}acquire方法总结:如果获取成功:state加1,调用AQSAbstractOwnableSynchronizer的父类设置独占线程,将当前独占线程设置为当前线程。如果调用失败:说明之前的线程已经占用了这个资源,需要释放等待的线程。然后将当前线程打包成一个node节点,放入node双向链表,然后Locksupport.pack()阻塞当前线程。如果线程被阻塞后被唤醒,会继续循环调用tryAcquire方法获取资源权限。如果获取到,则将自己的节点node设置为节点列表的头节点,并去掉之前的头节点。node节点的waitStatus为signal,表示它的下一个节点可以被唤醒。Release方法总结:如果线程释放资源,调用release方法,release方法会调用tryRelease方法尝试释放资源,如果释放成功,tryRelease方法会将state减1,然后调用AQSAbstractOwnableSynchronizer父类将独占线程设置为null,然后locksupport.unpack()恢复双向节点链表头节点线程的执行。4.按实际顺序打印ABC。/***@description:*@author:mmc*@create:2020-01-0309:42**/publicclassThreadABC{privatestaticObjectA=newObject();私有静态对象B=新对象();私有静态对象C=新对象();privatestaticclassThreadPrintextendsThread{私有字符串名称;私有对象私有对象自身;publicThreadPrint(Stringname,Objectprev,Objectself){this.name=name;这个.prev=prev;this.self=自我;}@Overridepublicvoidrun(){for(inti=0;i<10;i++){synchronized(prev){synchronized(self){System.out.println(name);self.notifyAll();}尝试{if(i>=9){prev.notifyAll();}else{prev.wai吨();}}catch(InterruptedExceptione){e.printStackTrace();}}}}}publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadPrintthreadA=newThreadPrint("A",C,A);ThreadPrintthreadB=newThreadPrint("B",A,B);ThreadPrintthreadC=newThreadPrint("C",B,C);threadA.start();线程.睡眠(10);threadB.start();线程.睡眠(10);threadC.start();}}
