05.java多线程问题

目录介绍

  • 5.0.0.1 线程池具备什么优势和缺点?为何说开启大量的线程,会下降程序的性能,那么该如何作才能下降性能?
  • 5.0.0.3 线程中start和run方法有什么区别?wait和sleep方法的不一样?sleep() 、join()、yield()有什么区别?
  • 5.0.0.4 用Java手写一个会致使死锁的程序,遇到这种问题解决方案是什么?那些场景用到了死锁机制?
  • 5.0.0.5 ThreadLocal(线程变量副本)这个类的做用是什么?
  • 5.0.0.6 什么是线程安全?线程安全有那几个级别?保障线程安全有哪些手段?ReentrantLock和synchronized的区别?
  • 5.0.0.7 Volatile和Synchronized各自用途是什么?有哪些不一样点?Synchronize在编译时如何实现锁机制?
  • 5.0.0.8 wait()和sleep()的区别?各自有哪些使用场景?怎么唤醒一个阻塞的线程?Thread.sleep(0)的做用是啥?
  • 5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分别有哪些使用场景?
  • 5.0.1.0 线程的有哪些状态?请绘制该状态的流程图?讲一下线程的执行生命周期流程?线程若是出现了运行时异常会怎么样?
  • 5.0.1.1 synchronized锁什么?synchronized同步代码块还有同步方法本质上锁住的是谁?为何?
  • 5.0.1.2 Volatile实现原理?一个int变量,用volatile修饰,多线程去操做++,线程安全吗?那如何才能保证i++线程安全?
  • 5.0.1.3 CAS原理是什么?CAS实现原子操做会出现什么问题?
  • 5.0.1.4 假若有n个网络线程,须要当n个网络线程完成以后,再去作数据处理,你会怎么解决?
  • 5.0.1.5 Runnable接口和Callable接口的区别?
  • 5.0.1.6 若是提交任务时,线程池队列已满,这时会发生什么?线程调度算法是什么?
  • 5.0.1.7 什么是乐观锁和悲观锁?
  • 5.0.1.8 线程类的构造方法、静态块是被哪一个线程调用的?同步方法和同步块,哪一个是更好的选择?同步的范围越少越好吗?
  • 5.0.1.9 synchonized(this)和synchonized(object)区别?Synchronize做用于方法和静态方法区别?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括Java基础及深刻知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,固然也在工做之余收集了大量的面试题,长期更新维护而且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 连接地址:https://github.com/yangchong211/YCBlogs
  • 若是以为好,能够star一下,谢谢!固然也欢迎提出建议,万事起于忽微,量变引发质变!全部博客将陆续开源到GitHub!

5.0.0.1 线程池具备什么优势和缺点?为何说开启大量的线程,会下降程序的性能,那么该如何作才能下降性能?

  • 线程池好处:
    • 1)下降资源消耗;
    • 2)提升相应速度;
    • 3)提升线程的可管理性。技术博客大总结
  • 线程池的实现原理:
    • 当提交一个新任务到线程池时,判断核心线程池里的线程是否都在执行。若是不是,则建立一个新的线程执行任务。若是核心线程池的线程都在执行任务,则进入下个流程。
    • 判断工做队列是否已满。若是未满,则将新提交的任务存储在这个工做队列里。若是工做队列满了,则进入下个流程。
    • 判断线程池是否都处于工做状态。若是没有,则建立一个新的工做线程来执行任务。若是满了,则交给饱和策略来处理这个任务。

5.0.0.3 线程中start和run方法有什么区别?wait和sleep方法的不一样?sleep() 、join()、yield()有什么区别?

  • 线程中start和run方法有什么区别
    • 为何咱们调用start()方法时会执行run()方法,为何咱们不能直接调用run()方法?这是一个很是经典的java多线程面试问题。当你调用start()方法时你将建立新的线程,而且执行在run()方法里的代码。可是若是你直接调用run()方法,它不会建立新的线程也不会执行调用线程的代码。
  • wait和sleep方法的不一样
    • 最大的不一样是在等待时wait会释放锁,而sleep一直持有锁。Wait一般被用于线程间交互,sleep一般被用于暂停执行。
  • 一、sleep()方法
    • 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操做受到系统计时器和调度程序精度和准确性的影响。 让其余线程有机会继续执行,但它并不释放对象锁。也就是若是有Synchronized同步块,其余线程仍然不能访问共享数据。注意该方法要捕获异常
    • 好比有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另外一个为MIN_PRIORITY,若是没有Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。
    • 总之,sleep()可使低优先级的线程获得执行的机会,固然也可让同优先级、高优先级的线程有执行的机会。
  • 二、yield()方法技术博客大总结
    • yield()方法和sleep()方法相似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行,另外yield()方法只能使同优先级或者高优先级的线程获得执行机会,这也和sleep()方法不一样。
  • 三、join()方法
    • Thread的非静态方法join()让一个线程B“加入”到另一个线程A的尾部。在A执行完毕以前,B不能工做。
    • Thread t = new MyThread(); t.start(); t.join();保证当前线程中止执行,直到该线程所加入的线程完成为止。然而,若是它加入的线程没有存活,则当前线程不须要中止。
  • Thread的join()有什么做用?
    • Thread的join()的含义是等待该线程终止,即将挂起调用线程的执行,直到被调用的对象完成它的执行。好比存在两个线程t1和t2,下述代码表示先启动t1,直到t1的任务结束,才轮到t2启动。
      t1.start();
      t1.join(); 
      t2.start();

5.0.0.4 用Java手写一个会致使死锁的程序,遇到这种问题解决方案是什么?那些场景用到了死锁机制?

  • 死锁是怎么一回事
    • 线程A和线程B相互等待对方持有的锁致使程序无限死循环下去。
  • 深刻理解死锁的原理
    • 两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock做为同步代码块的锁;
    • 线程1的run()方法中同步代码块先获取lock1的对象锁,Thread.sleep(xxx),时间不须要太多,50毫秒差很少了,而后接着获取lock2的对象锁。这么作主要是为了防止线程1启动一会儿就连续得到了lock1和lock2两个对象的对象锁
    • 线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,固然这时lock1的对象锁已经被线程1锁持有,线程2确定是要等待线程1释放lock1的对象锁的

5.0.0.5 ThreadLocal(线程变量副本)这个类的做用是什么?

  • ThreadLocal即线程变量
    • ThreadLocal为每一个线程维护一个本地变量。
    • 采用空间换时间,它用于线程间的数据隔离,它为每一个使用该变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。ThreadLocal的实现是以ThreadLocal对象为键。任意对象为值得存储结构。这个结构被附带在线程上,也就是说一个线程能够根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
  • ThreadLocal类是一个Map
    • ThreadLocal类中维护一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
    • ThreadLocal在Spring中发挥着巨大的做用,在管理Request做用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。
    • Spring中绝大部分Bean均可以声明成Singleton做用域,采用ThreadLocal进行封装,所以有状态的Bean就可以以singleton的方式在多线程中正常工做了。
  • 更多详细参考博客:深刻研究java.lang.ThreadLocal类

5.0.0.6 什么是线程安全?线程安全有那几个级别?保障线程安全有哪些手段?ReentrantLock和synchronized的区别?

  • 什么是线程安全
    • 线程安全就是当多个线程访问一个对象时,若是不用考虑这些线程在运行时环境下的调度和交替执行,也不须要进行额外的同步,或者在调用方进行任何其余的协调操做,调用这个对象的行为均可以得到正确的结果,那这个对象是线程安全的。
  • 线程安全也是有几个级别
    • 技术博客大总结
    • 不可变:
      • 像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新建立一个,所以这些不可变对象不须要任何同步手段就能够直接在多线程环境下使用
    • 绝对线程安全
      • 无论运行时环境如何,调用者都不须要额外的同步措施。要作到这一点一般须要付出许多额外的代价,Java中标注本身是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet
    • 相对线程安全
      • 相对线程安全也就是咱们一般意义上所说的线程安全,像Vector这种,add、remove方法都是原子操做,不会被打断,但也仅限于此,若是有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的状况下都会出现ConcurrentModificationException,也就是fail-fast机制。
    • 线程非安全技术博客大总结
      • ArrayList、LinkedList、HashMap等都是线程非安全的类.
  • 保障线程安全有哪些手段。保证线程安全可从多线程三特性出发:
    • 原子性(Atomicity):单个或多个操做是要么所有执行,要么都不执行
      • Lock:保证同时只有一个线程能拿到锁,并执行申请锁和释放锁的代码
      • synchronized:对线程加独占锁,被它修饰的类/方法/变量只容许一个线程访问
    • 可见性(Visibility):当一个线程修改了共享变量的值,其余线程可以当即得知这个修改
      • volatile:保证新值能当即同步到主内存,且每次使用前当即从主内存刷新;
      • synchronized:在释放锁以前会将工做内存新值更新到主存中
    • 有序性(Ordering):程序代码按照指令顺序执行
      • volatile: 自己就包含了禁止指令重排序的语义
      • synchronized:保证一个变量在同一个时刻只容许一条线程对其进行lock操做,使得持有同一个锁的两个同步块只能串行地进入
  • ReentrantLock和synchronized的区别
    • ReentrantLock与synchronized的不一样在于ReentrantLock:
      • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程能够选择放弃等待,改成处理其余事情。
      • 公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次得到锁。而synchronized是非公平的,即在锁被释放时,任何一个等待锁的线程都有机会得到锁。ReentrantLock默认状况下也是非公平的,但能够经过带布尔值的构造函数改用公平锁。
      • 锁绑定多个条件:一个ReentrantLock对象能够经过屡次调用newCondition()同时绑定多个Condition对象。而在synchronized中,锁对象wait()和notify()或notifyAl()只能实现一个隐含的条件,若要和多于一个的条件关联不得不额外地添加一个锁。
    • Synchronized是悲观锁机制,独占锁。而Locks.ReentrantLock是,每次不加锁而是假设没有冲突而去完成某项操做,若是由于冲突失败就重试,直到成功为止。
      • ReentrantLock适用场景
      • 某个线程在等待一个锁的控制权的这段时间须要中断
      • 须要分开处理一些wait-notify,ReentrantLock里面的Condition应用,可以控制notify哪一个线程,锁能够绑定多个条件。
      • 具备公平锁功能,每一个到来的线程都将排队等候。
    • 更多详细参考博客:Lock与synchronized 的区别

5.0.0.7 Volatile和Synchronized各自用途是什么?有哪些不一样点?Synchronize在编译时如何实现锁机制?

  • Volatile和Synchronized各自用途是什么?有哪些不一样点?
    • 1 粒度不一样,前者针对变量 ,后者锁对象和类
    • 2 syn阻塞,volatile线程不阻塞
    • 3 syn保证三大特性,volatile不保证原子性
    • 4 syn编译器优化,volatile不优化 volatile具有两种特性:
      • 1.保证此变量对全部线程的可见性,指一条线程修改了这个变量的值,新值对于其余线程来讲是可见的,但并非多线程安全的。
      • 2.禁止指令重排序优化。
    • Volatile如何保证内存可见性:
      • 1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
      • 2.当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
    • 同步:就是一个任务的完成须要依赖另一个任务,只有等待被依赖的任务完成后,依赖任务才能完成。
    • 异步:不须要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工做,只要本身任务完成了就算完成了,被依赖的任务是否完成会通知回来。(异步的特色就是通知)。 打电话和发短信来比喻同步和异步操做。
    • 阻塞:CPU停下来等一个慢的操做完成之后,才会接着完成其余的工做。
    • 非阻塞:非阻塞就是在这个慢的执行时,CPU去作其余工做,等这个慢的完成后,CPU才会接着完成后续的操做。
    • 非阻塞会形成线程切换增长,增长CPU的使用时间能不能补偿系统的切换成本须要考虑。
  • Synchronize在编译时如何实现锁机制?
    • Synchronized进过编译,会在同步块的先后分别造成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。若是这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。若是获取对象锁失败,那当前线程就要阻塞,直到对象锁被另外一个线程释放为止。

5.0.0.8 wait()和sleep()的区别?各自有哪些使用场景?怎么唤醒一个阻塞的线程?Thread.sleep(0)的做用是啥?

  • sleep来自Thread类,和wait来自Object类
    • 调用sleep()方法的过程当中,线程不会释放对象锁。而调用wait方法线程会释放对象锁
    • sleep睡眠后不出让系统资源,wait让出系统资源其余线程能够占用CPU
    • sleep(milliseconds)须要指定一个睡眠时间,时间一到会自动唤醒
  • 通俗解释
    • Java程序中wait 和 sleep都会形成某种形式的暂停,它们能够知足不一样的须要。wait()方法用于线程间通讯,若是等待条件为真且其它线程被唤醒时它会释放锁,而 sleep()方法仅仅释放CPU资源或者让当前线程中止执行一段时间,但不会释放锁。
  • 怎么唤醒一个阻塞的线程?
    • 若是线程是由于调用了wait()、sleep()或者join()方法而致使的阻塞,能够中断线程,而且经过抛出InterruptedException来唤醒它;若是线程遇到了IO阻塞,无能为力,由于IO是操做系统实现的,Java代码并无办法直接接触到操做系统。
    • 技术博客大总结
  • Thread.sleep(0)的做用是啥?
    • 因为Java采用抢占式的线程调度算法,所以可能会出现某条线程经常获取到CPU控制权的状况,为了让某些优先级比较低的线程也能获取到CPU控制权,可使用Thread.sleep(0)手动触发一次操做系统分配时间片的操做,这也是平衡CPU控制权的一种操做。

5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分别有哪些使用场景?

  • 同步和非同步
    • 同步和异步体现的是消息的通知机制:所谓同步,方法A调用方法B后必须等到方法B返回结果才能继续后面的操做;所谓异步,方法A调用方法B后可以让方法B在调用结束后经过回调等方式通知方法A
  • 阻塞和非阻塞
    • 阻塞和非阻塞侧重于等待消息时的状态:所谓阻塞,就是在结果返回以前让当前线程挂起;所谓非阻塞,就是在等待时可作其余事情,经过轮询去询问是否已返回结果

5.0.1.0 线程的有哪些状态?请绘制该状态的流程图?讲一下线程的执行生命周期流程?线程若是出现了运行时异常会怎么样?

  • 在任意一个时间点,一个线程只能有且只有其中的一种状态
    • 新建(New):线程建立后还没有启动
    • 技术博客大总结
    • 运行(Runable):包括正在执行(Running)和等待着CPU为它分配执行时间(Ready)两种
      无限期等待(Waiting):该线程不会被分配CPU执行时间,要等待被其余线程显式地唤醒。如下方法会让线程陷入无限期等待状态:
      没有设置Timeout参数的Object.wait()
      没有设置Timeout参数的Thread.join()
      LockSupport.park()
    • 限期等待(Timed Waiting):该线程不会被分配CPU执行时间,但在必定时间后会被系统自动唤醒。如下方法会让线程进入限期等待状态:
      Thread.sleep()
      设置了Timeout参数的Object.wai()
      设置了Timeout参数的Thread.join()
      LockSupport.parkNanos()
      LockSupport.parkUntil()
    • 阻塞(Blocked):线程被阻塞。和等待状态不一样的是,阻塞状态表示在等待获取到一个排他锁,在另一个线程放弃这个锁的时候发生;而等待状态表示在等待一段时间或者唤醒动做的发生,在程序等待进入同步区域的时候发生。
    • 结束(Terminated):线程已经结束执行
  • 绘制该状态的流程图
  • 线程若是出现了运行时异常会怎么样?
    • 若是这个异常没有被捕获的话,这个线程就中止执行了。另外重要的一点是:若是这个线程持有某个某个对象的监视器,那么这个对象监视器会被当即释放

5.0.1.1 synchronized锁什么?synchronized同步代码块还有同步方法本质上锁住的是谁?为何?

  • synchronized锁什么
    • 对于普通同步方法,锁是当前实例对象;
    • 对于静态同步方法,锁是当前类的Class对象;
    • 对于同步方法块,锁是括号中配置的对象;
    • 当一个线程试图访问同步代码块时,它首先必须获得锁,退出或抛出异常时必须释放锁。synchronized用的锁是存在Java对象头里的MarkWord,一般是32bit或者64bit,其中最后2bit表示锁标志位。
  • 本质上锁住的是对象。
    • 在java虚拟机中,每一个对象和类在逻辑上都和一个监视器相关联,synchronized本质上是对一个对象监视器的获取。当执行同步代码块或同步方法时,执行方法的线程必须先得到该对象的监视器,才能进入同步代码块或同步方法;而没有获取到的线程将会进入阻塞队列,直到成功获取对象监视器的线程执行结束并释放锁后,才会唤醒阻塞队列的线程,使其从新尝试对对象监视器的获取。

5.0.1.2 Volatile实现原理?一个int变量,用volatile修饰,多线程去操做++,线程安全吗?那如何才能保证i++线程安全?

  • volatile的做用和原理
    • Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终须要转化为汇编指令在CPU上执行。
    • volatile是轻量级的synchronized(volatile不会引发线程上下文的切换和调度),它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。
    • 因为内存访问速度远不及CPU处理速度,为了提升处理速度,处理器不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存后在进行操做,但操做完不知道什么时候会写到内存。普通共享变量被修改以后,何时被写入主存是不肯定的,当其余线程去读取时,此时内存中可能仍是原来的旧值,所以没法保证可见性。若是对声明了volatile的变量进行写操做,JVM就会想处理器发送一条Lock前缀的指令,表示将当前处理器缓存行的数据写回到系统内存。
  • 一个int变量,用volatile修饰,多线程去操做++,线程安全吗
    • 技术博客大总结
    • 不安全
    • 案例代码,至于打印结果就不展现呢
    • volatile只能保证可见性,并不能保证原子性。
    • i++实际上会被分红多步完成:
      • 1)获取i的值;
      • 2)执行i+1;
      • 3)将结果赋值给i。
    • volatile只能保证这3步不被重排序,多线程状况下,可能两个线程同时获取i,执行i+1,而后都赋值结果2,实际上应该进行两次+1操做。
      private volatile int a = 0;
      for (int x=0 ; x<=100 ; x++){
      new Thread(new Runnable() {
          @Override
          public void run() {
              a++;
              Log.e("小杨逗比Thread-------------",""+a);
          }
      }).start();
      }
  • 如何才能保证i++线程安全
    • 可使用java.util.concurrent.atomic包下的原子类,如AtomicInteger。其实现原理是采用CAS自旋操做更新值。
      for (int x=0 ; x<=100 ; x++){
      new Thread(new Runnable() {
          @Override
          public void run() {
              AtomicInteger atomicInteger = new AtomicInteger(a++);
              int i = atomicInteger.get();
              Log.e("小杨逗比Thread-------------",""+i);
          }
      }).start();
      }

5.0.1.3 CAS原理是什么?CAS实现原子操做会出现什么问题?

  • CAS原理是什么
    • CAS即compare and swap的缩写,中文翻译成比较并交换。CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。自旋就是不断尝试CAS操做直到成功为止。
  • CAS实现原子操做会出现什么问题
    • ABA问题。由于CAS须要在操做之的时候,检查值有没有发生变化,若是没有发生变化则更新,可是若是一个值原来是A,变成,有变成A,那么使用CAS进行检查时会发现它的值没有发生变化,但实际上发生了变化。ABA问题能够经过添加版本号来解决。Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。
    • 循环时间长开销大。pause指令优化。
    • 只能保证一个共享变量的原子操做。能够合并成一个对象进行CAS操做。

5.0.1.4 假若有n个网络线程,须要当n个网络线程完成以后,再去作数据处理,你会怎么解决?

  • 多线程同步的问题。这种状况能够可使用thread.join();join方法会阻塞直到thread线程终止才返回。更复杂一点的状况也可使用CountDownLatch,CountDownLatch的构造接收一个int参数做为计数器,每次调用countDown方法计数器减一。作数据处理的线程调用await方法阻塞直到计数器为0时。

5.0.1.5 Runnable接口和Callable接口的区别?

  • Runnable接口和Callable接口的区别
    • Runnable接口中的run()方法的返回值是void,它作的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合能够用来获取异步执行的结果。
    • 这实际上是颇有用的一个特性,由于多线程相比单线程更难、更复杂的一个重要缘由就是由于多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候咱们指望的数据是否已经赋值完毕?没法得知,咱们能作的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却能够获取多线程运行的结果,能够在等待时间太长没获取到须要的数据的状况下取消该线程的任务,真的是很是有用。

5.0.1.6 若是提交任务时,线程池队列已满,这时会发生什么?线程调度算法是什么?

  • 若是提交任务时,线程池队列已满,这时会发生什么?
    • 若是使用的是×××队列LinkedBlockingQueue,也就是×××队列的话,不要紧,继续添加任务到阻塞队列中等待执行,由于LinkedBlockingQueue能够近乎认为是一个无穷大的队列,能够无限存听任务
    • 技术博客大总结
    • 若是使用的是有界队列好比ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,会根据maximumPoolSize的值增长线程数量,若是增长了线程数量仍是处理不过来,ArrayBlockingQueue继续满,那么则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy
  • 线程调度算法是什么?
    • 抢占式。一个线程用完CPU以后,操做系统会根据线程优先级、线程饥饿状况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。

5.0.1.7 什么是乐观锁和悲观锁?

  • 什么是乐观锁和悲观锁?
    • 乐观锁:就像它的名字同样,对于并发间操做产生的线程安全问题持乐观状态,乐观锁认为竞争不老是会发生,所以它不须要持有锁,将比较-替换这两个动做做为一个原子操做尝试去修改内存中的变量,若是失败则表示发生冲突,那么就应该有相应的重试逻辑。
    • 悲观锁:仍是像它的名字同样,对于并发间操做产生的线程安全问题持悲观状态,悲观锁认为竞争老是会发生,所以每次对某资源进行操做时,都会持有一个独占的锁,就像synchronized,直接上了锁就操做资源。

5.0.1.8 线程类的构造方法、静态块是被哪一个线程调用的?同步方法和同步块,哪一个是更好的选择?同步的范围越少越好吗?

  • 线程类的构造方法、静态块是被哪一个线程调用的?
    • 线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。
  • 举个例子
    • 假设Thread2中new了Thread1,main函数中new了Thread2,那么:
      • Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2本身调用的
      • Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1本身调用的
  • 同步方法和同步块,哪一个是更好的选择?
    • 同步块,这意味着同步块以外的代码是异步执行的,这比同步整个方法更提高代码的效率。请知道一条原则:同步的范围越小越好。
    • 技术博客大总结
  • 同步的范围越少越好吗?
    • 是的。虽然说同步的范围越少越好,可是在Java虚拟机中仍是存在着一种叫作锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说StringBuffer,它是一个线程安全的类,天然最经常使用的append()方法是一个同步方法,咱们写代码的时候会反复append字符串,这意味着要进行反复的加锁->解锁,这对性能不利,由于这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,所以Java虚拟机会将屡次append方法调用的代码进行一个锁粗化的操做,将屡次的append的操做扩展到append方法的头尾,变成一个大的同步块,这样就减小了加锁-->解锁的次数,有效地提高了代码执行的效率。

5.0.1.9 synchonized(this)和synchonized(object)区别?Synchronize做用于方法和静态方法区别?

  • synchonized(this)和synchonized(object)区别技术博客大总结
    • 其实并无很大的区别,synchonized(object)自己就包含synchonized(this)这种状况,使用的场景都是对一个代码块进行加锁,效率比直接在方法名上加synchonized高一些(下面分析),惟一的区别就是对象的不一样。
    • 对synchronized(this)的一些理解
      • 1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。
      • 2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized(this)同步代码块。
      • 3、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)同步代码块的访问将被阻塞。
      • 4、当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。
  • Synchronize做用于方法和静态方法区别php

    • 测试代码以下所示html

      private void test() {
      final TestSynchronized test1 = new TestSynchronized();
      final TestSynchronized test2 = new TestSynchronized();
      Thread t1 = new Thread(new Runnable() {
      
          @Override
          public void run() {
              test1.method01("a");
              //test1.method02("a");
          }
      });
      Thread t2 = new Thread(new Runnable() {
      
          @Override
          public void run() {
              test2.method01("b");
              //test2.method02("a");
          }
      });
      t1.start();
      t2.start();
      }

    private static class TestSynchronized{
    private int num1;
    public synchronized void method01(String arg) {
    try {
    if("a".equals(arg)){
    num1 = 100;
    System.out.println("tag a set number over");
    Thread.sleep(1000);
    }else{
    num1 = 200;
    System.out.println("tag b set number over");
    }
    System.out.println("tag = "+ arg + ";num ="+ num1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }java

    private static int  num2;
    public static synchronized void method02(String arg) {
        try {
            if("a".equals(arg)){
                num2 = 100;
                System.out.println("tag a set number over");
                Thread.sleep(1000);
            }else{
                num2 = 200;
                System.out.println("tag b set number over");
            }
            System.out.println("tag = "+ arg + ";num ="+ num2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    }git

    //调用method01方法打印日志【普通方法】
    tag a set number over
    tag b set number over
    tag = b;num =200
    tag = a;num =100github

    //调用method02方法打印日志【static静态方法】
    tag a set number over
    tag = a;num =100
    tag b set number over
    tag = b;num =200面试

    - 在static方法前加synchronized:静态方法属于类方法,它属于这个类,获取到的锁,是属于类的锁。 
    - 在普通方法前加synchronized:非static方法获取到的锁,是属于当前对象的锁。 [技术博客大总结](https://github.com/yangchong211/YCBlogs)
    - 结论:类锁和对象锁不一样,synchronized修饰不加static的方法,锁是加在单个对象上,不一样的对象没有竞争关系;修饰加了static的方法,锁是加载类上,这个类全部的对象竞争一把锁。

其余介绍

01.关于博客汇总连接

02.关于个人博客

相关文章
相关标签/搜索