多线程复习

多线程中的wait与sleep到底谁释放了锁
首先,多线程中会使用到两个延迟的函数,wait和sleep。 
wait是Object类中的方法,而sleep是Thread类中的方法。

sleep是Thread类中的静态方法。无论是在a线程中调用b的sleep方法,还是b线程中调用a的sleep方法,谁调用,谁睡觉。 
最主要的是sleep方法调用之后,并没有释放锁。使得线程仍然可以同步控制。sleep不会让出系统资源; 
而wait是进入线程等待池中等待,让出系统资源。

调用wait方法的线程,不会自己唤醒,需要线程调用 notify / notifyAll 方法唤醒等待池中的所有线程,才会进入就绪队列中等待系统分配资源。sleep方法会自动唤醒,如果时间不到,想要唤醒,
可以使用interrupt方法强行打断。 Thread.sleep(
0) // 触发操作系统立刻重新进行一次CPU竞争。 使用范围: sleep可以在任何地方使用。而wait,notify,notifyAll只能在同步控制方法或者同步控制块中使用。 sleep必须捕获异常,而wait,notify,notifyAll的不需要捕获异常。
wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,
只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放),调用wait方法的一个或多个线程就会解除wait状态,
重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。  2)当前线程必须拥有此对象的monitor(即锁),才能调用某个对象的wait()方法能让当前线程阻塞, (这种阻塞是通过提前释放synchronized锁,重新去请求锁导致的阻塞,这种请求必须有其他线程通过notify()或者notifyAll()唤醒重新竞争获得锁)  3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程; (notify()或者notifyAll()方法并不是真正释放锁,必须等到synchronized方法或者语法块执行完才真正释放锁)  4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程,唤醒的线程获得锁的概率是随机的,取决于cpu调度
yield()
会放弃CPU资源,锁资源不会放弃的。但是当同步代码执行完毕,资源锁自然就释放了。

yield() 方法和释放不释放锁没有关系

jdk 中的解释为:

调用该方法的线程通知线程调度器当前线程可以让出CPU,线程调度器可以响应或者忽略此请求。

要注意的是:

线程调度器并不一定响应这个请求。

响应请求时,仅仅将当前线程变为可运行状态。其他处于可运行状态的线程将竞争CPU资源,高优先级线程将会比相同优先级的线程有较高的概率获得CPU资源,但并不保证。

另外,需要注意的是,CPU资源和锁的获取并没有直接关系,CPU资源是由系统来分配的。
实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
join()
线程实例的join()方法可以使得一个线程在另一个线程结束后再执行,即也就是说使得当前线程可以阻塞其他线程执行;

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
问:Java多线程运行环境中,在哪些情况下会使对象锁释放?
答:由于等待一个锁的线程只有在获得这把锁之后,才能恢复运行,所以让持有锁的线程在不再需要锁的时候及时释放锁是很重要的。在以下情况下,持有锁的线程会释放锁:
(1)执行完同步代码块,就会释放锁。(synchronized)
(2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
(3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进
       入对象的等待池。(wait)

除了以上情况以外,只要持有锁的线程还没有执行完同步代码块,就不会释放锁。
在下面情况下,线程是不会释放锁的:
(1)执行同步代码块的过程中,执行了Thread.sleep()方法,当前线程放弃CPU,开始睡眠,在睡眠中不会释放锁。
(2)在执行同步代码块的过程中,执行了Thread.yield()方法,当前线程放弃CPU,但不会释放锁。
(3)在执行同步代码块的过程中,其他线程执行了当前线程对象的suspend()方法,当前线程被暂停,但不会释放锁。
sleep执行后线程进入阻塞状态
yield执行后线程进入就绪状态
join执行后线程进入阻塞状态
join()中止当前线程(也就是a),等待指定(也就是b)线程结束,然后再运行当前线程

 

线程的资源有不少,但应该包含CPU资源和锁资源这两类。
sleep(long mills):让出CPU资源,但是不会释放锁资源。
wait():让出CPU资源和锁资源。
锁是用来线程同步的,sleep(long mills)虽然让出了CPU,但是不会让出锁,其他线程可以利用CPU时间片了,但如果其他线程要获取sleep(long mills)拥有的锁才能执行,则会因为无法获取锁而不能执行,继续等待。
但是那些没有和sleep(long mills)竞争锁的线程,一旦得到CPU时间片即可运行了。

 

posted @ 2020-03-16 20:38  何浩源  阅读(69)  评论(0编辑  收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中