• Geek_1f1a07
    2019-06-25
    Zed说的不对,首先,所有的锁,无论synchronize还是lock,如果发生竞争条件,都可能造成上下文切换,优化锁的目的是为了尽量降低发生锁竞争的概率,synchronize做的优化都是把竞争的可能消灭在前期的偏向锁,轻量级锁,把会造成上下文切换的“脏活”留在最后。lock的乐观锁大体思路也是一样的,不到万不得已,不会轻易调用park方法。但是本质上java目前都是利用内核线程,所以都会有上下文切换。除非使用协程的技术,这个以前有green thread,后来不用了,期待老师后面对协程的讲解。

    作者回复: 回答很好,赞一个。

    
     18
  • QQ怪
    2019-06-26
    我觉得有些人建议使用notifyall的原因是使用notify需要有十足的把握去确认哪条线程需要唤醒,因为一不留神就容易搞错,为了优化而优化最后事倍功半,所以大家才会使用notifyall一劳永逸,我其实挺认同老师的观点,老师,全部唤醒会导致更多的上下文切换,是否要优化这点,我觉得还是得看个人了吧😂

    作者回复: notify()可以结合wait(long)方法使用,解决某些没有通知的线程被通知不到的问题

     2
     6
  • WL
    2019-06-25
    老师请问一下在一段程序中除了工作线程之外还有很多守护线程, 这些线程加起来的数量必然比cup的数量会多很多, 那么为什么创建线程池的时候要参考CPU的数量呢, 为什么不把守护线程也考虑进去呢?
    
     5
  • Zed
    2019-06-25
    回答趙衍同学

    如你所说,synchronized主要是因为有用户态和内核态的交互所以能到进程级别。

    而Lock是通过AQS的state状态来判断是否持有锁,整个过程都是在用户态或者说纯java实现。

    最后lock.await()也是把当前线程放到当前条件变量的等待队列中并让出cpu。顺便提下,lock支持多条件变量。
    展开

    作者回复: 回答很好。线程进入阻塞,两者都会发生进程上下文切换。Synchronized中阻塞线程无论何时去获取锁,都需要进入到内核态,而AQS中,阻塞线程再次获取锁时,是通过state以及CAS操作判断,只有没有竞争成功时,才会再次被挂起,这样可以尽量减少上下文切换。

    
     5
  • 疯狂咸鱼
    2019-09-21
    volitile的读写不会导致上下文切换,操作系统层面怎么理解呢

    作者回复: volatile主要是用来保证共享变量额可见性,以及防止指令重排序,保证执行的有序性。

    通过生成.class文件之后,反编译文件我们可以看到通过volatile修饰的共享变量,在写入操作的时候会多一个Lock前缀这样的指令,当操作系统执行时会由于这个指令,将当前处理器缓存的数据写回系统内存中,并通知其他处理器中的缓存失效。

    所以volatile不会带来线程的挂起操作,不会导致上下文切换。

    
     3
  • K
    2019-07-22
    老师好,我有个特别简单的小问题不太明白。既然用了vector,为什么还要用synchronize锁起来啊,vector本身不就是线程安全的?谢谢老师回答。

    作者回复: 这里的vector是一个对象锁,锁的是一个代码块,并不是保证vector的线程安全。

    
     3
  • 💪😊
    2019-06-25
    多个软件共同运行也有可能导致上下文切换,有些软件考虑使用绑定固定cpu核方式运行
    
     2
  • td901105
    2019-12-20
    老师,是不是使用Lock锁机制不会有用户态和内核态的切换?还是Lock本身锁机制是不涉及用户态到内核态的切换的,只是在未获取锁的时候需要使用内核态的方法比如park方法进行线程的挂起?

    作者回复: 一样会有,相对同步锁来说,只是减少了用户态和内核态的切换。Lock锁被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成。

    
     1
  • 皮皮
    2019-06-25
    老师您好,一直有个疑问想请教,就是JDK1.5引入的lock锁底层实现也是调用了lockhelper的park和unpark方法,这个是否也涉及到系统的上下文切换,用户态和内核态的切换?

    作者回复: 是的

    
     1
  • 趙衍
    2019-06-25
    老师好!在synchronized中,“挂起”这个动作是由JVM来实现的,获取不到锁的线程会被迫让出CPU,由于synchronized是基于操作系统的mutex机制,所以会产生进程的上下文切换。我想请问老师,在JDK的Lock中,或者AQS中,线程“挂起”这个动作又是怎么实现的呢?为什么不会产生进程级别的上下文切换呢?

    作者回复: AQS挂起是通过LockSupport中的park进入阻塞状态,这个过程也是存在进程上下文切换的。但被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成,这样就比AQS阻塞的线程更消耗系统资源了。

    
     1
  • 阿杜
    2019-12-27
    减少上下文切换:1)notify优化,减少notify访问,设置wait时间;2)线程数设置合理;3)减少jvm垃圾回收
    
    
  • vivi
    2019-09-30
    请问,那事物的开启和锁的持有时间该如何权衡?例如我方法里设计到三个不同表的插入更新操作,其中一个是库存表,是该把锁加在事物外面还是在事物中进行库存表的加锁操作

    作者回复: 在事务内加,其实这种情况使用乐观锁来锁库存会性能好一些

    
    
  • Young
    2019-09-19
    请问老师,线程wait区分是由于等待超时重新运行为什么不需要再去重新获取锁呢,我的理解是,wait后锁被释放了,那线程重新恢复运行后无论什么情况下都应该先去获取锁

    作者回复: wait是在锁代码块里面,所以一旦超时,则会跳出该同步锁代码块

    
    
  • godtrue
    2019-09-09
    这节很不错,不过疑问还是有的
    感觉老师没有完全讲清楚进程的上下文切换和线程的上下文切换?另外,老师对于什么是进程?什么是线程?他们之间的区别与联系也是没有讲的比较细致?这两个概念非常重要,不过能通俗易懂的讲明白的不多。另外,不管进程还是线程我认为若想被CPU执行,少不了要进入内核态。进进出出比较费劲但又不得不进,那就减少进进出出的次数,少进为妙,少进的方法就是少触发那些进进出出的条件。比如:减少锁持有时间,减少锁粒度,少触发锁竞争,减少FULL GC,减少IO阻塞,创建合适的线程数等等。

    作者回复: 进程的上下文切换指的是用户态和内核态的相互切换,后续补上进程的上下文切换。

     1
    
  • 风轻扬
    2019-08-27
    老师,如果将文中的例子的wait,notify,notifyAll,全部换成Condition的await,signal和signalAll方法。我试了一下,只要把synchronized换成lock锁就可以实现生产10个数据,然后消费10个数据。有两个问题请教您,望老师不吝指教
    1 .就这个功能来说,直接一对一替换api,有什么需要注意的地方吗?我第一次使用Condition
    2 .如果生产者await等待超时,再次获取锁的行为,需要怎么做呢?
    
    
  • devin.ou
    2019-07-21
    老师,能给出wait/notify优发后的代码么

    作者回复: 你好 devin,在第29讲中讲到了具体的优化。

    
    
  • Demon.Lee
    2019-07-14
    1. ‘’在 JDK1.6 中,JVM 将 Synchronized 同步锁分为了偏向锁、轻量级锁、偏向锁以及重量级锁,优化路径也是按照以上顺序进行。‘’
     这句话里面有两个偏向锁,后一个是不是“自旋锁”呀

    2. 老师只说了减少垃圾回收频率可以减少上下文切换,没说如何减少回收频率。感觉不是个好问题,算了,我还是先去Google下找答案吧。

    作者回复: 你好DemonLee同学,第一句是偏向锁、轻量级锁、自旋锁以及重量级锁;
    第22讲中详细讲解了优化垃圾回收的内容,可以跳过去先了解下。

    
    
  • 旭东
    2019-07-04
    在 JDK1.6 中,JVM 将 Synchronized 同步锁分为了偏向锁、轻量级锁、偏向锁以及重量级锁,优化路径也是按照以上顺序进行。JIT 编译器在动态编译同步块的时候,也会通过锁消除、锁粗化的方式来优化该同步锁。

    老师,这个只是JDK1.6的优化,还是1.6以后都是这么优化的?

    作者回复: 包括了JDK1.6

    
    
  • 奇奇
    2019-07-03
    不同时执行remive 但是也会进去执行remove(0)也是不符合语义的

    作者回复: 是的,这个循环应该放到锁里面。已修正,谢谢提醒。

    
    
  • 奇奇
    2019-07-02
    代码写错了
    while(pool.isEmpty())不能放在同步代码块的外面
    假设此时pool不为空容量为1,此时10个线程的pool.isEmpty都为false,此时全部跳出循环。
    全部执行pool.remove(0) 错误

    编辑回复: 同学你好!后面有个锁,不会同时进去remove。如有疑问,可继续留言。

    
    
我们在线,来聊聊吧