Java多线程面试题与答案

线程

线程与进程的区别是什么?

  • 进程指的是应用程序在操作系统中执行的副本(系统分配资源的最小单位),线程是程序执行的最小单位;
  • 进程使用独立的数据空间,而线程共享进程的数据空间。

线程状态图

img

多线程会带来哪些性能问题?

  • 调度开销,一般线程数往往大于CPU核心数,这样操作系统再执行线程时就会出现上下文切换,从而产生一定性能开销;
  • 协作开销,为了保证线程之间共享变量的线程安全,有可能会禁用编译器和CPU的重排序等优化,还可能会频繁的将工作内存刷新到主内存,主内存再同步给工作内存,这些开销都是单线程下不存在的。

JMM内存模型

什么是JMM内存模型?

  • JMM 是和多线程相关的一组规范,需要各个 JVM 的实现来遵守 JMM 规范;
  • JMM 与处理器、缓存、并发、编译器有关。它解决了 CPU 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。

什么是指令重排序?有什么好处?

  • 重排序是指编译器、JVM 或者 CPU 为了提高执行效率,对于实际指令执行的顺序进行调整;
  • 重排序通过减少执行指令,从而提高整体的运行速度。

什么是内存可见性问题?

  • 共享变量的值已经被第 1 个线程修改了,但是其他线程却看不到。

主内存和工作内存的关系是什么?

  • 所有的变量都存储在主内存中,同时每个线程拥有自己独立的工作内存,而工作内存中的变量的内容是主内存中该变量的拷贝;
  • 线程不能直接读 / 写主内存中的变量,但可以操作自己工作内存中的变量,然后再同步到主内存中,这样,其他线程就可以看到本次修改;
  • 主内存是由多个线程所共享的,但线程间不共享各自的工作内存,如果线程间需要通信,则必须借助主内存中转来完成。

什么是 happens-before 关系?

  • 如果第一个操作和第二个操作之间满足 happens-before 关系,那么我们就说第一个操作对于第二个操作一定是可见的;

volatile

volatile的作用是什么?

  • 保证内存可见性以及多线程之间操作的有序性

volatile如何保证可见性?

  • volatile变量修饰的共享变量,在进行写操作的时候会多出一个lock前缀的汇编指令,当对其进行写操作时,JVM就会向处理器发送一条Lock前缀的指令,把这个变量所在的缓存行的数据写回到系统内存。然后处理器会根据MESI缓存一致性协议来保证多CPU下的各个高速缓存中的数据的一致性。

volatile是否可以保证原子性?

  • volatile是一种轻量级的同步机制,它主要有两个特性:
    • 保证共享变量对所有线程的可见性;
    • 禁止指令重排序优化;
  • 同时需要注意的是,在多线程场景下,如果仅仅是赋值操作,volatile可以保证原子性,但是像num++这种复合操作(取值、计算、赋值),volatile无法保证其原子性。

synchronized

synchronized有几种使用方式?

  • 类、方法、代码块

synchronized的底层实现原理是什么?

  • 每个Object对象中都内置了一个Monitor监视器,通过指令Monitor.enter和Monitor.exit进行加锁和释放锁,加锁失败的线程会被加入到一个同步队列中,当锁被释放时再重新竞争锁。

JVM对synchronized做了哪些优化?

  • 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

描述锁升级的过程

  • 偏向锁升级轻量级锁:当一个对象持有偏向锁,一旦第二个线程访问这个对象,如果产生竞争,偏向锁升级为轻量级锁。
  • 轻量级锁升级重量级锁:一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。

wait和notify为什么需要在synchronized里面?

  • wait方法的语义有两个,一个是释放当前的对象锁、另一个是使得当前线程进入阻塞队列,而这些操作都和监视器是相关的,所以wait必须要获得一个监视器锁。
  • 而对于notify来说也是一样,它是唤醒一个线程,既然要去唤醒,首先得知道它在哪里,所以就必须要找到这个对象获取到这个对象的锁,然后到这个对象的等待队列中去唤醒一个线程。

wait/notify 和 sleep 方法的区别是什么?

  • wait 方法必须在 synchronized 保护的代码中使用,而 sleep 方法并没有这个要求;
  • 在同步代码中执行 sleep 方法时,并不会释放 monitor 锁,但执行 wait 方法时会主动释放 monitor 锁;
  • sleep 方法中会要求必须定义一个时间,时间到期后会主动恢复,而对于没有参数的 wait 方法而言,意味着永久等待,直到被中断或被唤醒才能恢复,它并不会主动恢复;
  • wait/notify 是 Object 类的方法,而 sleep 是 Thread 类的方法。

为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?

  • 因为 Java 中每个对象都有一把称之为 monitor 监视器的锁,锁信息保存在对象头中,wait/notify/notifyAll 都是锁级别的操作,所以把它们定义在 Object 类中是最合适;
  • 如果把 wait/notify/notifyAll 方法定义在 Thread 类中,会带来很大的局限性,比如一个线程可能持有多把锁,以便实现相互配合的复杂逻辑,假设此时 wait 方法定义在 Thread 类中,如何实现让一个线程持有多把锁呢?又如何明确线程等待的是哪把锁呢?既然我们是让当前线程去等待某个对象的锁,自然应该通过操作对象来实现,而不是操作线程。

synchronize与volatile的区别是什么?

  • volatile 可以看作是一个轻量版的 synchronized,比如一个共享变量如果自始至终只被各个线程赋值和读取,而没有其他操作的话,那么就可以用 volatile 来代替 synchronized 或者代替原子变量,足以保证线程安全;
  • volatile无法保证对“i++”一类复合操作(包括取值、计算、赋值)的原子性和互斥性,它保证了变量间的可见性,并禁用了指令重排序;
  • synchronize没有禁用指令重排序,这也是单例double check模式,对象必须用volatile修饰的原因。

AQS

什么是AQS,内部组成有哪些?

  • AQS提供了一个FIFO双向队列,可以看做是一个用来实现锁以及其他需要同步功能的框架。
  • AQS主要由三部分组成:
    • 第一个是 state,它是一个数值,在不同的类中表示不同的含义,往往代表一种状态;
    • 第二个是一个FIFO的队列,该队列用来存放阻塞状态的线程;
    • 第三个是“获取/释放”的相关方法,需要利用 AQS 的工具类根据自己的逻辑去实现。

AQS的底层结构具体是怎样的?

  • 底层是由head节点、tail节点、双向链表组成的双向队列;
  • head与tail节点主要负责节点的出队与入队,时间复杂度O(1);
  • 之所以使用双向链表而不是单向链表,是因为AQS考虑到高并发的场景下,节点的状态时刻都有可能发生变化,当前节点的一些动作需要依赖前序节点的状态,例如:
    • 只有当前节点的prev节点为head时,才有资格参与锁竞争;
    • 当前节点进入阻塞之前需要判断该节点的prev节点的状态是否为SIGNAL(节点的线程释放或被取消会通知后继节点)。

AQS解决了哪些问题?

  • 状态的原子性管理;
  • 线程的阻塞与解除阻塞;
  • 队列的管理。

AQS中state的应用有哪些?

  • 对于ReentrantLock,持有锁的线程每次lock重入,state+1,每次unlock,state -1,只有state = 0才表示彻底释放锁,其它线程才可以获取;
  • 对于Semaphore,acquire 方法代表获取许可,此时能不能获取许可取决于state的值是否足够,如果足够state值会减掉对应的许可数量,如果不够则会进入阻塞,release方法代表释放许可,state值会增加直到定义的上限值;
  • 对于CountDownLatch,await方法会判断state值是否为0,不为0则进入阻塞等待,直到其它线程通过countDown方法将state减为0才会执行;
  • 对于CyclicBarrier,线程调用await方法state会+1,如果state值小于初始设置的阈值,线程阻塞等待,直到state累加等于该阈值,所有等待的线程会一起释放,同时state会清0。

Lock

Lock和synchronized的区别?

类别synchronizedLock
存在层次Java的关键字,在jvm层面上是一个接口
锁的释放1、以获取锁的线程执行完同步代码,释放锁;2、线程执行发生异常,jvm会让线程释放锁。必须在finally中释放锁,不然容易造成线程死锁
锁的获取假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待。Lock有多种获取锁的方式,如lock、tryLock
锁状态无法判断,只能阻塞可以判断;tryLock();tryLock(long time, TimeUnit unit);可避免死锁。
锁类型可重入,非公平,不可中断可重入,可公平(两者皆可)可中断:lockInterruptibly();
功能功能单一API丰富;tryLock();tryLock(long time, TimeUnit unit);可避免死锁。

描述Lock的加锁的全流程

  • 当一个线程成功地获取了同步状态(或者锁),其他线程将无法获取,转而被构造成为尾节点并加入AQS同步队列,这个过程通过CAS来保证的线程安全。
  • 同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点。
  • 设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置头节点的方法并不需要使用CAS来保证,它只需要将首节点设置成为原首节点的后继节点并断开原首节点的next引用即可。

公平锁与非公平锁的区别,如何实现的?

  • 非公平锁在获取锁的时候,会先通过CAS进行抢占,而公平锁则不会;
  • 公平锁会优先从同步队列中去唤醒,这样就保证了先到先得的顺序;
  • 非公平锁的效率更高,因为唤醒线程的过程是比较耗时的,非公平锁会利用这部分时间完成其它任务,但有可能造成锁饥饿。

对比悲观锁,乐观锁的优点和缺点都有哪些?

  • 乐观锁优点:
    • 悲观锁需要遵循下面三种模式:一锁、二读、三更新,即使在没有冲突的情况下,执行也会非常慢;
    • 乐观锁本质上不是锁,它只是一个判断逻辑,资源冲突少的情况下,它不会产生任何开销;
  • 乐观锁缺点:
    • 在并发量比较高的情况下,有些线程可能会一直尝试修改某个资源,但由于冲突比较严重,一直更新不成功,这时候,就会给 CPU 带来很大的压力(并发量大可以考虑通过分段锁的方式优化,例如LongAdder,或者直接使用悲观锁);
    • 无法解决ABA问题,意思是指在 CAS 操作时,有其他的线程现将变量的值由 A 变成了 B,然后又改成了 A,当前线程在操作时,发现值仍然是 A,于是进行了交换操作。大部分场景下ABA问题不会给业务带来影响,可以通过引入版本号的方式解决。

线程池

线程池的核心参数有哪些?

public ThreadPoolExecutor(   int corePoolSize, // 核心线程数                          int maximumPoolSize, // 最大线程数  long keepAliveTime, // 临时线程等待时间                   TimeUnit unit, // 时间单位               BlockingQueue<Runnable> workQueue, // 阻塞队列      RejectedExecutionHandler handler) // 拒绝策略

线程池执行任务的流程是什么?

img

拒绝策略有哪些?

  • AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常;
  • DiscardPolicy:丢弃任务,但是不抛出异常;
  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中;
  • CallerRunsPolicy:提交任务的主线程调用任务的run()方法,绕过线程池直接执行(这种方法不会发生数据丢失,并且可以延缓任务提交的速度,缓解线程池压力)。

ForkJoinPool有什么特点?

  • 它每个线程都有一个自己的双端队列来存储分裂出来的子任务,避免了公共队列的阻塞;
  • 采用工作窃取模式,空闲线程 t1 可以帮助繁忙线程 t0 完成工作,这也是队列设计为双端队列的目的,t0是按LIFO的顺序处理任务,而t1 在steal t0任务时会按照FIFO的顺序;
  • ForkJoinPool 非常适合用于递归的场景,例如树的遍历、最优路径搜索等场景。

ForkJoinPool与ThreadPoolExecutor区别是什么?

  • ForkJoinPool中的每个线程都会有一个队列,而ThreadPoolExecutor只有一个队列,并根据queue类型不同,细分出各种线程池;
  • ForkJoinPool能够使用数量有限的线程来完成非常多的具有父子关系的任务,ThreadPoolExecutor中根本没有什么父子关系任务;
  • ForkJoinPool在多任务,且任务分配不均是有优势,但是在单线程或者任务分配均匀的情况下,效率没有ThreadPoolExecutor高。

JDK提供的线程池用到了哪些阻塞队列?

  • LinkedBlockingQueue:FixedThreadPool 和 SingleThreadExector 的默认队列,容量为 Integer.MAX_VALUE,可以认为是无界队列,不会生成多于核心线程数的线程;
  • SynchronousQueue:CachedThreadPool的默认队列,是一种没有容量的阻塞队列。与FixedThreadPool正好相反,CachedThreadPool为了尽可能创建新的线程执行任务,它的最大线程数是 Integer.MAX_VALUE,队列容量为0;
  • DelayedWorkQueue:ScheduledThreadPool的默认队列,可以周期性执行任务或延迟一定时间执行任务,DelayedWorkQueue会按照延迟的时间长短对任务排序,内部数据结构是堆(二叉树)。

CPU核心数和线程数的关系是什么?

  • 如果是CPU密集型任务,例如加密、解密、编译、压缩、计算等任务,一般可以考虑线程数为CPU核心数的1~2倍,具体还应该参考压测结果;
  • 如果是IO密集型任务,可参考公式:线程数 = CPU 核心数 *(1 + 线程平均等待时间 / 线程平均工作时间)。

队列

队列常见api的区别?

img

有哪些常见的阻塞队列?

  • ArrayBlockingQueue
    • 最典型的有界队列,其内部是用数组存储元素的,不会扩容,利用 ReentrantLock 实现线程安全;
  • LinkedBlockingQueue
    • 内部用链表实现的 BlockingQueue,容量默认就为整型的最大值 Integer.MAX_VALUE,一般称为无界队列;
  • SynchronousQueue
    • 容量为0的传递队列,存数据会阻塞,知道有人来存,同理取数据也会阻塞,直到有人来存;
  • PriorityBlockingQueue
    • 无界的优先级阻塞队列(有初始容量,可扩容),可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则,内部的数据结构是堆;
  • DelayQueue
    • 无界的延迟队列,DelayQueue 内部使用了 PriorityQueue 的能力来进行排序。

ArrayBlockingQueue的实现原理是什么?

  • ArrayBlockingQueue 实现并发同步的原理就是利用 ReentrantLock 和它的两个 Condition,读操作和写操作都需要先获取到 ReentrantLock 独占锁才能进行下一步操作;
  • 进行读操作时如果队列为空,线程就会进入到读线程专属的 notEmpty 的 Condition 的队列中去排队,等待写线程写入新的元素;
  • 同理,如果队列已满,这个时候写操作的线程会进入到写线程专属的 notFull 队列中去排队,等待读线程将队列元素移除并腾出空间。

阻塞队列和非阻塞队列在实现上有哪些区别?

  • 阻塞队列最主要是利用了 ReentrantLock 以及它的 Condition 来实现,而非阻塞队列则是利用 CAS 方法实现线程安全。

多线程工具类

CountDownLatch与CyclicBarrier的区别是什么?

  • 作用对象不同:CyclicBarrier 要等固定数量的线程都到达了栅栏位置才能继续执行,而 CountDownLatch 只需等待数字倒数到 0,也就是说 CountDownLatch 作用于事件,但 CyclicBarrier 作用于线程;
  • 可重用性不同:CountDownLatch 在倒数到 0 并且触发门闩打开后,就不能再次使用了,除非新建一个新的实例;而 CyclicBarrier 可以重复使用,并不需要重建实例。CyclicBarrier 还可以随时调用 reset 方法进行重置,如果重置时有线程已经调用了 await 方法并开始等待,那么这些线程则会抛出 BrokenBarrierException 异常。
  • 执行动作不同:CyclicBarrier 有执行动作 barrierAction,而 CountDownLatch 没这个功能。

Future

Future与CompletableFuture区别?

  • 通过Future接收异步任务时,主线程需要通过get()方法去阻塞轮询获取结果,如果是多个任务,则需要等到所有任务完成之后才能做后续操作;
  • 而通过CompletableFuture接收异步任务时,无需等待所有任务全部完成,每个任务都可以通过thenAccept、thenApply、thenCompose等方式将前面的结果交给另一个异步事件来处理,最后还可以通过allOf或anyOf等方法来汇总结果。

FutureTask的实现原理是什么?

  • 当我们通过Future接收异步任务时,底层是通过其实现类FutureTask的run方法来执行任务的,run方法主要完成了以下流程:
    • 检查线程的状态,执行用户定义的call方法;
    • 执行结束后,设置返回值或异常,并更新线程状态,然后唤醒队列中阻塞等待获取结果的线程;
  • 当其它线程通过future.get获取结果时:
    • 首先根据状态判断任务是否完成,如果已经完成则直接返回;
    • 如果没有完成则会阻塞当前线程,并将其加入到阻塞队列(如果没有指定阻塞时间则一直阻塞知道任务完成唤醒);
    • 当任务完成时,阻塞的线程会被唤醒,拿到结果后返回;

ThreadLocal

ThreadLocal的作用与使用场景是什么?

  • ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
  • ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过 ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。

ThreadLocal与Thread的关系是什么?

一个 Thread 里面只有一个ThreadLocalMap ,而在一个 ThreadLocalMap 里面却可以有很多的 ThreadLocal,每一个 ThreadLocal 都对应一个 value。因为一个 Thread 是可以调用多个 ThreadLocal 的,所以 Thread 内部就采用了 ThreadLocalMap 这样 Map 的数据结构来存放 ThreadLocal 和 value。

ThreadLocal与Synchronized的区别是什么?

  • ThreadLocal 是通过让每个线程独享自己的副本,避免了资源的竞争。
  • synchronized 主要用于临界资源的分配,在同一时刻限制最多只有一个线程能访问该资源。
  • ThreadLocal 并不是用来解决共享资源的多线程访问的问题,因为每个线程中的资源只是副本,并不共享。因此ThreadLocal适合作为线程上下文变量,简化线程内传参。

ThreadLocal为什么可能产生内存泄漏,如何避免?

  • 通过ThreadLocalMap的源码可以看到,Entry中的key被定义为弱引用类型,当发生GC时,key会被直接回收,无需手动清理。
  • 而value属于强引用类型,被当前的Thread对象关联,所以说value的回收取决于Thread对象的生命周期。
    • 如果说一个线程执行完毕,线程Thread随之被释放,那么value便不存在内存泄漏的问题。
    • 然而,我们一般会通过线程池的方式来复用Thread对象来节省资源,这就会导致一个Thread对象的生命周期会非常长,随着任务的执行,value就有可能越来越多且无法释放,最终导致内存泄漏。
  • 因此,我们在使用完ThreadLocal变量后,要手动调用remove()方法来清理ThreadLocalMap(一般在finally代码块中)。

子线程如何共享主线程的ThreadLocal变量?

  • 因为ThreadLocal变量保存在当前线程的成员变量ThreadLocalMap中,新创建子线程后无法再次使用父线程的ThreadLocal变量;
  • 为了解决子线程复用主线程ThreadLocal的问题,Thread类中还有另一个ThreadLocalMap:inheritableThreadLocals,里面保存的是InheritableThreadLocal,它是ThreadLocal的子类,Thread类初始化时会默认从父线程继承inheritableThreadLocals;
  • 因此我们可以用InheritableThreadLocal代替ThreadLocal实现父子线程共享线程变量的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM面试题与答案

JVM内存布局 JVM在内存布局上可以分为哪些区域&#xff1f; 堆&#xff08;线程共享&#xff09;&#xff1a;GC的主要回收地&#xff0c;包含几乎所有的实例对象、字符串常量池&#xff1b;元空间&#xff08;线程共享&#xff09;&#xff1a;在本地内存分配&#xff0c;包…

md0和md1linux软raid,软RAID管理命令mdadm详解

mdadm是linux下用于创建和管理软件RAID的命令&#xff0c;是一个模式化命令。但由于现在服务器一般都带有RAID阵列卡&#xff0c;并且RAID阵列卡也很廉价&#xff0c;且由于软件RAID的自身缺陷(不能用作启动分区、使用CPU实现&#xff0c;降低CPU利用率)&#xff0c;因此在生产…

Dubbo常见面试题与答案

Dubbo的基础知识 Dubbo的核心架构是怎样的&#xff1f; Registry&#xff1a;注册中心。 负责服务地址的注册与查找&#xff0c;服务的 Provider 和 Consumer 只在启动时与注册中心交互。注册中心通过长连接感知 Provider 的存在&#xff0c;在 Provider 出现宕机的时候&#…

Redis常见面试题与答案

Redis的基本数据类型 Redis有哪些常用的数据类型&#xff1f; String&#xff1a;字符串&#xff08;最常用的缓存&#xff09;Hash&#xff1a;哈希&#xff08;保存对象&#xff09;List&#xff1a;有序列表&#xff08;消息队列&#xff09;Set&#xff1a;无序集合&…

c语言利用文件体写在桌面上,在C语言中怎样新建一个文件夹?

满意答案JacinthLancet推荐于 2017.10.12采纳率&#xff1a;56% 等级&#xff1a;12已帮助&#xff1a;35899人函数名: mkdir功 能: 建立一个目录(文件夹)用 法: int mkdir(char *pathname);程序例: (在win-tc和Dev-c下运行通过)#include #include #include int main(void){…

MySQL常见面试题与答案

存储引擎 InnoDB的主要特点是什么&#xff1f; MySQL5.5版本之后的默认存储引擎&#xff1b;支持事务&#xff1b;支持行级锁&#xff1b;支持MVCC&#xff1b;支持聚集索引方式存储数据。 InnoDB与MyISAM的区别&#xff1f; 存储引擎MyISAMInnoDB存储结构MyISAM在磁盘上存…

Spring 异常处理三种方式

Spring 异常处理三种方式 异常处理方式一. ExceptionHandler异常处理方式二. 实现HandlerExceptionResolver接口异常处理方式三. ControllerAdviceExceptionHandler三种方式比较说明(强烈推荐各位看一下&#xff0c;我觉得自己总结的比较多&#xff0c;嘿嘿&#xff0c;不对之…

Netty常见面试题 与 答案

Netty基础知识 什么是Netty&#xff1f; Netty 是一款用于高效开发网络应用的 NIO 网络框架&#xff0c;它大大简化了网络应用的开发过程&#xff1b; 封装了JDK底层的NIO模型&#xff0c;提供高度可用的API&#xff0c;用于快速开发高性能服务端和客户端&#xff1b;精心设计…

CAS单点登录详细流程

一、CAS简介和整体流程 CAS 是 Yale 大学发起的一个开源项目&#xff0c;旨在为 Web 应用系统提供一种可靠的单点登录方法&#xff0c;CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点&#xff1a; 【1】开源的企业级单点登录解决方案。 【2】CAS Server 为…

android 图片自动拉伸,解决关于ImageView自适应的问题(拉伸变形,图片模糊)

今天接手一个项目发现有个地方的图片显示非常小&#xff0c;而且还不够清晰&#xff0c;也没办法自适应屏幕的显示&#xff0c;非常的影响美观&#xff0c;于是 就像这优化一下。先看看优化前的效果和优化后的效果。修复后修复前布局文件修复前&#xff1a;修复前布局文件修复后…

redisTemplate.opsForValue()中方法讲解

查看源码可以看到redisTemplate.opsForValue()中的方法都定义在ValueOperations<K, V> 中&#xff0c;该接口中一共有17个方法&#xff1a; public interface ValueOperations<K, V> {void set(K key, V value);void set(K key, V value, long timeout, TimeUnit …

spring boot配置dubbo(properties)

spring boot与dubbo配置(properties) dubbo和zookeeper配合使用&#xff0c;具体的它们之间的配置这里不说了。 一、spring boot与dubbo配置有两种方式&#xff1a; 1&#xff09;spring boot在自己的配置文件application.properties 配置dubbo。&#xff08;本篇主要说这个&…

spring boot配置dubbo(XML)

上一篇写的是spring boot在自己的properties配置文件中简单配置dubbo的步骤&#xff0c;那种配置有很多的功能&#xff08;比如超时时间、是否检查&#xff09;等等&#xff0c;配置起来也挺麻烦的&#xff0c;而我们也习惯传统的那种XML形式的dubbo配置。 这一篇写的是spring…

spring boot配置dubbo注意事项

spring boot配置dubbo注意事项 通过前两篇文章&#xff0c;知道了spring boot配置dubbo有两种方式。具体请回顾前两篇文章吧。 现在主要是说下spring boot在自己的application.properties 文件里配置dubbo内容&#xff0c;这种方式遇到的问题。 问题一&#xff1a;dubbo接口…

spring boot配置mybatis和事务管理

spring boot配置mybatis和事务管理 一、spring boot与mybatis的配置 1.首先,spring boot 配置mybatis需要的全部依赖如下&#xff1a; <!-- Spring Boot 启动父依赖 --> <parent><groupId>org.springframework.boot</groupId><artifactId>spr…

android hud sdk,Android HUD SDK | 百度地图API SDK

简介为了给用户提供更安全优质的服务&#xff0c;LBS开放平台针对Android平台的SDK产品引入Key认证机制&#xff0c;用户在使用之前需要先申请配置Key&#xff0c;并在程序相应位置填写您的Key。Key机制&#xff1a;每个Key仅且唯一对于1个应用验证有效&#xff0c;即对该Key配…

spring boot配置druid(德鲁伊)

spring boot配置druid(德鲁伊) 关于druid的介绍请看 阿里巴巴温少访谈 1.引入相关依赖&#xff0c;全部依赖是上一篇spring bootmybatis依赖的基础上&#xff0c;再加上下边的依赖&#xff0c;如下&#xff1a; <!-- Druid数据库连接池组件 --> <dependency><…

spring boot----简单入门

Spring Boot其实就是spring,为了简便spring框架的搭建&#xff0c;快速开发项目而推出的另一种方式。使用spring boot&#xff0c;搭建框架将变得特别快。可以有更多的时间在代码性能上优化了。 一、Spring Boot个人感觉的优点: ​ 1&#xff09;搭建一个简单运行的框架很快&…

android js 代码混淆工具,好用的JS(Javascript)混淆加密工具-HDS JSObfuscator 2.14版

好用的JS(Javascript)混淆加密工具-HDS JSObfuscator 2.14版书法字体2017.06.23css压缩分享一款好用的JS混淆加密工具:HDS JSObfuscator 2.14版&#xff0c;HDS JSObfuscator兼顾了JS压缩、JS混淆和JS加密三大功能。js加密工具的主要作用就是为了保护Javascript代码&#xff0c…

Maven 建立父子项目和跨项目调用内容的步骤

使用Maven管理项目&#xff0c;同时我们是做微服务&#xff0c;所以对于微服务项目来说&#xff0c;使用Maven建立起一个父子项目这种关系&#xff0c;很方便管理&#xff08;只是个人感觉&#xff09; 设计层面 &#xff08;1&#xff09;服务端和客户端肯定是分开写的&…