面试题并发编程三大特性原子性多个操作执行期间不会发生上下文切换。可见性线程操作JVM主内存数据时会先从主内存中拿取在工作内存中计算完之后再同步会主内存同步到主内存之前的结果其他线程不可见。可以通过volatile实现。有序性CPU在执行java指令时底层会进行优化导致指令重排序。可以通过volatile实现。锁分类悲观锁与乐观锁获取悲观锁后其他线程会挂起。乐观锁不涉及线程挂起例如CAS(存在ABA、自旋次数过多两个问题)。可重入锁与不可重入锁当期线程获取锁之后是否还可以继续获取该锁。公平锁与非公平锁先排队的线程先获取锁互斥锁与共享锁是否允许多个线程同时持有。synchronized优化JDK1.6版本中synchronized就做了大量的优化和ReentrantLock性能差不多了。①锁消除不需要加锁操作的可以直接将锁操作去掉。②锁粗化锁范围扩大。在循环内部加的锁资源这样会导致至少要执行100万次的加锁和释放锁的操作太过频繁。锁膨胀的优化会将synchronized锁的范围膨胀到for循环之外这样一来加锁一次完成100万次循环的业务操作然后释放锁一次即可大大滴提升了性能。③锁升级JDK1.5时获取锁失败就直接挂起了导致线程阻塞。锁升级解决尽量降低出现线程阻塞的情况。偏向锁 轻量级锁 重量级锁。偏向锁如果一个锁基本上只有一个线程抢占持有那这个线程下一次获取锁时直接获取即可不需要进行CAS判断。轻量级锁获取锁失败后会先进行CAS尝试直到达到自旋阈值。重量级锁传统的synchronized方式如果拿锁成功走人拿锁失败线程挂起。ReentrantLock和synchronized的区别synchronized拿不到锁只能死等。ReentrantLock提供了多种方式获取锁资源。synchronized只支持非公平锁。ReentrantLock支持公平锁和非公平锁默认是非公平锁。synchronized底层是基于对象实现的。ReentrantLock是基于AQS抽象类实现的。零加载properties文件publicclassTest{publicstaticvoidmain(String[]args)throwsIOException{PropertiespropertiesnewProperties();// resources目录下InputStreamisTest.class.getClassLoader().getResourceAsStream(abc.properties);try{properties.load(is);System.out.println(properties.get(key1));ResourceBundlebundleResourceBundle.getBundle(abc);System.out.println(bundle.getString(key1));}catch(IOExceptione){e.printStackTrace();}finally{is.close();}}}WebService跨编程语言、跨操作系统的远程调用技术可用于发布服务端接口供不同的客户端调用。Java中有三种WebService规范主要使用的是JAX-WSReentrantLock.Condition用于实现等待通知机制。集合容量是3集合已满时生产者等待集合已空时消费者等待。publicclass等待通知机制{staticReentrantLocklocknewReentrantLock();staticConditionconditionlock.newCondition();staticLinkedListIntegerlistnewLinkedList();staticAtomicBooleanabnewAtomicBoolean(true);publicstaticvoidmain(String[]args){newThread(()-{for(inti0;i100;i){lock.lock();try{while(list.size()3){condition.await();}list.addLast(i);System.out.println(生产者生产 i);condition.signalAll();if(i99){ab.set(false);}}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}}}).start();newThread(()-{while(ab.get()){lock.lock();try{while(list.size()0){condition.await();}System.out.println(消费者消费 list.removeFirst());condition.signalAll();}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}}}).start();}}一、ReentrantLockAbstractQueuedSynchronizer(AQS)抽象队列同步器属性state锁的状态。在ReentrantLock中state0代表当前锁未被持有state1当前当前锁被某个线程持有state1代表当前锁不仅被某个线程持有而且被重入。exclusiveOwnerThread保存持有当前锁的线程。head、tail等待队列中的头节点跟尾节点。Node节点属性有thread当前等待节点的线程实例prev上一个节点。next下一个节点。waitStatus当前等待节点的状态。如果waitStatus 0代表当前节点的线程释放锁后立马唤醒头节点的下一个节点。lock()publicfinalvoidacquire(intarg){if(!tryAcquire(arg)acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}lock()中公平锁与非公平锁的区别公平锁直接调用acquire();而非公平锁直接尝试CAS将state从0替换成1如果成功设置exclusiveOwnerThread当前线程逻辑结束如果失败acquire();1、tryAcquire() 尝试获取锁①如果state0尝试CAS将state从0替换为1如果替换成功将exclusiveOwnerThread赋为当前线程当前线程成功获取到锁逻辑结束。②判断持有锁的线程是否是当前线程如果是当前线程state锁重入成功逻辑结束。tryAcquire()中公平锁与非公平锁的区别非公平锁在state0时直接尝试CAS将state从0替换成1而公平锁会判断有没有前驱结点(head.next.thread!当前线程)如果没有前驱结点才会尝试CAS将state从0替换成1。2、addWaiter() 加入等待队列①根据当前线程创建Node节点。②如果tailnull说明等待队列为空CAS将head从null替换成new Node()如果替换成功tailhead③如果tail不为null循环CAS将tail替换为node替换成功后原tail.nextnodenode.pre原tailPS如果第二步成功还是会走第三步。3与4在acquireQueued()中finalbooleanacquireQueued(finalNodenode,intarg){booleanfailedtrue;try{booleaninterruptedfalse;for(;;){finalNodepnode.predecessor();if(pheadtryAcquire(arg)){setHead(node);p.nextnull;// help GCfailedfalse;returninterrupted;}if(shouldParkAfterFailedAcquire(p,node)parkAndCheckInterrupt())interruptedtrue;}}finally{if(failed)cancelAcquire(node);}}acquireQueued()逻辑大概是在循环里先走第四步if prev head tryAcquire()如果成功逻辑结束如果失败走第三步shouldParkAfterFailedAcquire()线程阻塞如果被唤醒继续走循环里的逻辑。3、shouldParkAfterFailedAcquire() 线程阻塞①如果上一个节点的waitStatus0往上遍历将上一个节点移除node.prev node.prev.prev直到上一个节点的waitStatus不大于0②如果上一个节点的waitStatus!-1通过CAS循环将上一个节点的waitStatus替换成-1③如果上一个节点的waitStatus-1了通过LockSupport.park()阻塞当前节点的线程4、if prev head tryAcquire() 被唤醒后尝试获取锁①当前线程被唤醒后如果上一个节点是头节点再次通过第一步tryAcquire()去尝试获取锁获取锁成功后让头节点headnode②如果上一个节点不是head或者tryAcquire()获取锁失败后再次通过第三步shouldParkAfterFailedAcquire()线程阻塞直到再次被唤醒。tryLock()逻辑与lock()的第一步tryAcquire()一致不管公平锁或者非公平锁走的都是非公平锁的tryAcquire()。tryLock(long timeout, TimeUnit unit)tryAcquireNanos()publicfinalbooleantryAcquireNanos(intarg,longnanosTimeout){if(Thread.interrupted())thrownewInterruptedException();returntryAcquire(arg)||doAcquireNanos(arg,nanosTimeout);}doAcquireNanos()privatebooleandoAcquireNanos(intarg,longnanosTimeout){finallongdeadlineSystem.nanoTime()nanosTimeout;finalNodenodeaddWaiter(Node.EXCLUSIVE);booleanfailedtrue;try{for(;;){finalNodepnode.predecessor();if(pheadtryAcquire(arg)){setHead(node);p.nextnull;// help GCfailedfalse;returntrue;}nanosTimeoutdeadline-System.nanoTime();if(nanosTimeout0L)returnfalse;if(shouldParkAfterFailedAcquire(p,node)nanosTimeoutspinForTimeoutThreshold)LockSupport.parkNanos(this,nanosTimeout);if(Thread.interrupted())thrownewInterruptedException();}}finally{if(failed)cancelAcquire(node);// 有一步是将waitStatus改成1}}与lock()步骤类似区别在于第三步shouldParkAfterFailedAcquire() 线程阻塞会有阻塞时间到达阻塞时间后自动唤醒不会一直阻塞。第四步if prev head tryAcquire() 被唤醒后尝试获取锁如果获取锁失败会先判断是否超时如果超时直接获取锁失败如果没超时才会走第三步线程阻塞。unlock()release()publicfinalbooleanrelease(intarg){if(tryRelease(arg)){Nodehhead;if(h!nullh.waitStatus!0)unparkSuccessor(h);returntrue;}returnfalse;}1、tryRelease()①判断持有锁的线程是否是当前线程如果不是抛出异常。②state–如果state减完之后不等于0逻辑结束。如果state减完之后等于0设置持有锁的线程等于null走第二步。2、unparkSuccessor()①如果头节点的waitStatus不等于0找出头节点下的第一个waitStatus≤0的节点通过LockSupport.unpark()唤醒节点。二、ThreadPoolExecutor七大参数2maximumPoolSizekeepAliveTimecorePoolSizeBlockingQueueTimeUnitThreadFactoryRejectHandler1、AtomicInteger ctl前三位代表线程池状态runState后29为当前线程池的线程个数workCount线程池状态从小到大依次为running-1、shutdown0、stop1、tidying2、terminated3running接受新任务并且可以执行队列中的任务。shutdown不接收新任务但是可以执行队列中的任务。stop(shutdownNow())不接收新任务并且不执行队列中的任务。tidying线程池状态变为shutdown、stop后当所有任务已终止工作线程数为0线程池状态会自动更新为tidying状态并执行terminated()方法。terminated执行完terminated()方法之后线程池状态会自动更新为terminated状态。2.workers工作线程集合。参数配置最大线程数配置计算型任务CPU核心数 1IO型任务CPU核心数 * 2计算公式线程数 CPU核心数 * (1 线程等待时间 / 线程运行总时间)CPU核心数获取方法Runtime.getRuntime().availableProcessors();核心线程数配置如果是长期并发量都特别高的业务可以将核心线程数与最大线程数都设置为计算出来的线程数。如果是偶尔并发量特别高的业务可以将核心线程数设置的小一点最大线程数设置为计算出来的线程数。任务队列大小配置根据自己能接受的最长等待时间决定假如核心线程数是100每个任务耗时1s则任务队列中的第1000个任务可能需要等待10s才能执行到。execute()publicvoidexecute(Runnablecommand){if(commandnull)thrownewNullPointerException();intcctl.get();if(workerCountOf(c)corePoolSize){if(addWorker(command,true))return;cctl.get();}if(isRunning(c)workQueue.offer(command)){intrecheckctl.get();if(!isRunning(recheck)remove(command))reject(command);elseif(workerCountOf(recheck)0)addWorker(null,false);}elseif(!addWorker(command,false))reject(command);}1、if (workCount corePoolSize) addWorker()如果工作线程数小于核心线程数通过addWorker(runnable,true)创建核心工作线程2、if (runState running) workQueue.offer(runnable)①如果不满足第一点即工作线程数大于等于核心线程数时②判断当前线程池状态是否是running如果是则将任务添加到任务队列③如果添加成功判断工作线程数是否等于0如果等于0需要通过addWorker(null,false)创建一个工作线程用于处理任务。3、if (workCount corePoolSize workQueue is full) addWorker(runnable,false)如果工作线程数大于等于核心线程数并且工作队列是满的则通过addWorker(runnable,false)创建非核心线程。如果创建非核心线程失败说明当前的工作线程数已经达到了最大线程数走拒绝策略。addWorker(runnable,boolean)用于创建工作线程。如果runnable ! null此时创建的工作线程用于执行execute()中传递来的任务并且执行工作队列中的任务。如果runnable null此时创建的工作线程仅仅用于执行工作队列中的任务。1、if (runState running || (runState shutdown runnablenull workQueue isNotEmpty)) continue;①如果线程池状态是运行中则可以正常创建工作线程。②如果线程池的状态不是运行中但是是shutdown此时虽然不能接收新任务但是可以工作队列中的任务再判断runnable是否为null并且工作队列是否不为空如果都满足说明要创建的工作线程仅仅用于执行工作队列中的任务。③除了以上两种情况直接return false;2、判断当前工作线程数是否超过核心线程数或者最大线程数(方法参数决定)3、new Worker() workers.add(worker) workCount thread.start()①创建工作线程其中工作线程封装了线程工厂创建的线程。②将工作线程放到workers集合中③工作线程数1④调用worker对象中的线程对象的start()方法开启线程thread的run()方法中会调用worker对象的runWorker()方法。runWorker(worker)1、while(firstTask ! null || getTask() ! null)①firstTask是创建工作线程时指定的任务会首先执行。②firstTask执行完之后不断从任务队列中取任务③如果任务为nullfinally里调用processWorkerExit()方法该工作线程结束2、runnable.run()①执行任务。②如果任务抛异常了会走到finally中finally里调用processWorkerExit()方法该工作线程结束。3、processWorkerExit() TODO:JRB①workCount–②workers.remove(worker)getTask()get runnable from workQueue判断该工作线程是不是非核心线程(workCount corePoolSize)①如果是非核心线程通过Runnable r workQueue.poll(keepAliveTime)阻塞获取队列中的任务其中等待时间是非核心线程的最大空闲时间。②如果是核心线程通过Runnable r workQueue.take()一直阻塞的拉取任务。③如果拉取到任务(r!null)直接返回该任务。④如果没有拉取到任务(rnull)说明在非核心线程的最大空闲时间内没有拉取到任务。此时判断当前线程是否是非核心线程(workCount corePoolSize)如果还是return null三、JUC1、atomic包AtomicBooleanAtomicIntegervalue用volatile修饰。底层调用unsafe类的方法CAS循环。LongAddrAtomicReferenceAtomicMarkableReference保证reference与boolean的原子性。简单解决ABA问题。StuzsnewStu(zs,18);AtomicMarkableReferenceStuamrnewAtomicMarkableReference(zs,true);boolean[]markHoldernewboolean[1];Stustuamr.get(markHolder);StuexpectedReferencestu;StunewReferencenewStu(ls,29);booleanexpectedMarkmarkHolder[0];booleannewMark!expectedMark;booleanresultamr.compareAndSet(expectedReference,newReference,expectedMark,newMark);AtomicStampedReference保证reference与int的原子性。解决ABA问题。StuzsnewStu(zs,18);AtomicStampedReferenceStuamrnewAtomicStampedReference(zs,1);int[]stampHoldernewint[1];Stustuamr.get(stampHolder);StuexpectedReferencestu;StunewReferencenewStu(ls,29);intexpectedStampstampHolder[0];intnewStampexpectedStamp1;booleanresultamr.compareAndSet(expectedReference,newReference,expectedStamp,newStamp);2、locks包ReentrantLock(AQS、LockSupport)ReentrantReadWriteLock3、直接JUC下ThreadPoolExecutorCompletableFuture// 创建方式CompletableFutureVoidc1CompletableFuture.runAsync(()-System.out.println(123),threadPool);CompletableFutureIntegerc2CompletableFuture.supplyAsync(()-1,threadPool);作用一多个任务同时操作都会有返回结果让这些任务先执行主线程等待等所有任务执行完之后对每个任务的执行结果进行处理。CompletableFuture.supplyAsync()封装到listCompletableFuture.allOf(list).join()CompletableFuture.get()获取结果作用二任务串行执行执行完A任务后返回结果作为参数调用handle()执行B任务。作用三任务的交互thenCombine() 两个任务的结果作为参数创建新的任务去执行。CountDownLatchawait()阻塞当前线程直到执行了指定次数的countDown()Exchanger两个线程直接交换数据。CyclicBarrier创建CyclicBarrier时可以传递runnable在等待被唤醒后先执行该runnable后线程再往下执行。await()方法会阻塞直到到达执行数量的await()方法都被执行才会唤醒。CopyOnWriteArrayListConcurrentHashMapArrayBlockingQueueLinkedBlockingQueueAPI使用ScheduledExecutorServicepublicstaticvoidmain(String[]args)throwsInterruptedException{ScheduledExecutorServiceschedulerExecutors.newScheduledThreadPool(2);System.out.println(~~~~~newDate());// 延迟2s后执行一次性任务scheduler.schedule(()-{System.out.println(~~~~~~~~~);},2,TimeUnit.SECONDS);// 延迟5s开始执行任务第一个任务开始时间隔2s开始执行第二个任务可能存在两个任务同时执行scheduler.scheduleAtFixedRate(()-{try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(~~~~~~~~newDate());},5,2,TimeUnit.SECONDS);// 延迟5s开始执行任务第一个任务结束后间隔2s开始执行第二个任务不可能存在两个任务同时执行scheduler.scheduleWithFixedDelay(()-{try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(~~~~~~~~newDate());},5,2,TimeUnit.SECONDS);}3、