wait 和 sleep 的区别

两个方法:Object 中定义的wait方法和Thread类中定义的sleep方法的区别?
相同之处:都可能让线程从Runnble状态进入Timed-Waiting状态

wait sleep
定义在Object类 定义在Thread类
必须要先持有锁,并且会释放锁 不释放锁
改变线程状态为:WAITING 或 TIMED_WAITING 改变线程状态为:TIMED_WAITING
放在同步代码块中才可以wait,并且是在锁对象上调用wait方法 代码位置没有限制,在线程类上调用sleep方法
状态从WAITING 可能变会 RUNNABLE,但也可能变为BLOCKED,取决于有没有拿到锁 状态直接变回RUNNABLE,跟锁无关

如果当前线程没持有锁,而直接在锁对象上调用wait方法,会抛出如下异常
image.png
但是,如果直接调用Thread.sleep方法,是不会有异常的。

  1. /**
  2. * wait 和 sleep 的区别
  3. *
  4. */
  5. public class Demo3 {
  6. public static void main(String[] args) throws InterruptedException {
  7. // sleep 没有代码位置的限制
  8. Thread.sleep(1000);
  9. // 创建一个对象,并且这个对象就是一把锁
  10. Object lock = new Object();
  11. // lock.wait(); //Exception in thread "main" java.lang.IllegalMonitorStateException
  12. synchronized (lock){ // main 线程去申请锁,锁是lock对象
  13. // 创建一个线程 , 申请获取同一把锁
  14. new Thread(new Runnable() {
  15. @Override
  16. public void run() {
  17. // 等待一秒钟,在申请锁
  18. try {
  19. TimeUnit.SECONDS.sleep(1);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. synchronized (lock){
  24. // 模拟业务处理
  25. System.out.println(Thread.currentThread().getName()+":获取到了锁");
  26. // 唤醒所有在该锁上wait的线程
  27. lock.notifyAll();
  28. }
  29. }
  30. },"A").start();
  31. // 做一些业务处理
  32. System.out.println(Thread.currentThread().getName()+":获取到到了锁");
  33. // 做完本线程的任务之后,想要释放锁,让其他线程有机会那到这个锁,就可以wait
  34. lock.wait(); // 线程会进入WAITING 状态
  35. System.out.println(Thread.currentThread().getName()+":运行结束");
  36. }
  37. }
  38. }

synchronize 关键字

作用:如果某个线程想要进入synchronize关键字修饰的代码块或者方法,那么该线程必须要先抢到synchronize关键字对应的那把锁。相当于同一时刻,加了synchronize关键字的代码块或者方法,只能由一个线程进行执行。

使用场景 锁是什么
修饰代码块 代码中显式指定一个锁对象
修饰实例方法 锁是实例对象 this
修饰静态方法 锁是一个Class对象(类名.class)

修饰代码块的语法:

  1. // 同步代码块 , 同一时间只允许一个线程进入同步代码块
  2. synchronized (锁对象){
  3. }

错误代码示范:

  1. public class SyncDemo01 {
  2. public void test1(){
  3. synchronized (new Object()){
  4. for (int i=0;i<20;i++) {
  5. System.out.println(Thread.currentThread().getName()+":"+i);
  6. }
  7. }
  8. }
  9. public static void main(String[] args) {
  10. final SyncDemo01 syncDemo01 = new SyncDemo01();
  11. final Thread threadA = new Thread(() -> syncDemo01.test1(),"A");
  12. final Thread threadB = new Thread(() -> syncDemo01.test1(),"B");
  13. threadA.start();
  14. threadB.start();
  15. }
  16. }
  1. /**
  2. * 当使用 synchronized 修饰实例方法时, 锁是什么? 锁就是this对象
  3. */
  4. public synchronized void test1(){
  5. }
  1. public synchronized static void test2(){
  2. }
  1. package com.qf.thread;
  2. public class SyncDemo01 {
  3. public Object lock = new Object();
  4. public void test1(){
  5. synchronized (SyncDemo01.class){
  6. for (int i=0;i<20;i++) {
  7. System.out.println(Thread.currentThread().getName()+":"+i);
  8. }
  9. }
  10. }
  11. // 锁对象是什么呢? this
  12. public synchronized void test2(){
  13. for (int i=0;i<20;i++) {
  14. System.out.println(Thread.currentThread().getName()+":"+i);
  15. }
  16. }
  17. // synchronized 修饰静态方法,锁是什么呢? 是当前类的class对象
  18. // final Class<SyncDemo01> syncDemo01Class = SyncDemo01.class;
  19. // SyncDemo01.class是不是单例? 是的 。 当jvm类加载器加载SyncDemo01的时候,自动创建了该class对象,并且是单例
  20. public synchronized static void test3(){
  21. for (int i=0;i<20;i++) {
  22. System.out.println(Thread.currentThread().getName()+":"+i);
  23. }
  24. }
  25. public static void main(String[] args) {
  26. final SyncDemo01 o1 = new SyncDemo01();
  27. final Thread threadA = new Thread(() -> o1.test1(),"A");
  28. final Thread threadB = new Thread(() -> o1.test3(),"B");
  29. threadA.start();
  30. threadB.start();
  31. }
  32. public static void p2(){
  33. final SyncDemo01 o1 = new SyncDemo01();
  34. final Thread threadA = new Thread(() -> o1.test1(),"A");
  35. final Thread threadB = new Thread(() -> o1.test2(),"B");
  36. threadA.start();
  37. threadB.start();
  38. }
  39. public static void p1(){
  40. final SyncDemo01 o1 = new SyncDemo01();
  41. final SyncDemo01 o2 = new SyncDemo01();
  42. final Thread threadA = new Thread(() -> o1.test1(),"A");
  43. final Thread threadB = new Thread(() -> o2.test1(),"B");
  44. threadA.start();
  45. threadB.start();
  46. }
  47. }

思考案例:
开启两个线程,执行实例方法m,方法m为循环打印1-20个数字。在不开启同步的情况下,会是什么效果?
如果开启synchronized同步,会是什么效果?
注意:同步的原则,必须在相同的锁对象上进行同步!

思考:使用同步代码块还是使用同步方法呢?

  1. package com.qf.sy2103.thread02;
  2. /**
  3. * 思考:使用同步代码块还是使用同步方法呢?
  4. * 推荐用法:尽可能减少同步代码的范围。在必须要进行同步的地方使用 代码块方式开启同步。
  5. */
  6. public class ThreadDemoChoose {
  7. public void test1(String path){
  8. //1. 读取指定 path的文件 ,并把文件的内容读取出来进行分析
  9. //2. 把分析的结果写入到某个指定的文件中
  10. synchronized (this){
  11. }
  12. }
  13. }

思考:持有锁的线程如果出现异常,锁是否会被释放?(练习)

  1. /**
  2. * synchronize 到底锁的是什么
  3. * synchronized 代表的语义是开启一段同步代码块
  4. * 同步代码块:一个特殊的代码块,只能有一个线程进入到同步代码块中,其他线程进不来
  5. * 为什么线程能进入同步代码块呢? 能进入的线程,一定是申请到了可以进入同步代码块的锁
  6. * 锁是什么? 一个java对象,可以是锁。 类.class ,Demo4.class 可以是一把锁。
  7. */
  8. package com.qf.sy2103.thread02;
  9. import lombok.SneakyThrows;
  10. public class SynchronizedDemo {
  11. public static void test3(){
  12. System.out.println("test3 stated..");
  13. synchronized (SynchronizedDemo.class){ // 可以使用 当前实例对象 (this)
  14. for (int i = 0; i < 100; i++) {
  15. System.out.println(Thread.currentThread().getName() +":" + i);
  16. }
  17. }
  18. }
  19. /**
  20. * 锁是什么 ? 类名.class
  21. * 当类 SynchronizedDemo 被jvm 加载之后,会在堆内存中创建一个对象,这个对象就是 SynchronizedDemo.class
  22. * 是一个 类型为 Class<SynchronizedDemo> 的对象
  23. */
  24. public synchronized static void test2(){
  25. System.out.println("test2 start..");
  26. for (int i = 0; i < 100; i++) {
  27. System.out.println(Thread.currentThread().getName() +":" + i);
  28. }
  29. }
  30. public void test0() throws InterruptedException {
  31. // final Object lock = new Object();
  32. System.out.println("test0 stated..");
  33. synchronized (this){ // 可以使用 当前实例对象 (this)
  34. for (int i = 0; i < 100; i++) {
  35. System.out.println(Thread.currentThread().getName() +":" + i);
  36. }
  37. }
  38. }
  39. /**
  40. * 当使用 synchronized 修饰实例方法时, 锁是什么? 锁就是this对象
  41. */
  42. public synchronized void test1(){
  43. System.out.println("test1 start..");
  44. for (int i = 0; i < 100; i++) {
  45. System.out.println(Thread.currentThread().getName() +":" + i);
  46. }
  47. }
  48. public static void main(String[] args) {
  49. // p1();
  50. // p2();
  51. final Thread t1 = new Thread(new Runnable() {
  52. @Override
  53. public void run() {
  54. SynchronizedDemo.test2();
  55. }
  56. });
  57. final Thread t2 = new Thread(new Runnable() {
  58. @Override
  59. public void run() {
  60. SynchronizedDemo.test3();
  61. }
  62. });
  63. t1.setName("t1");
  64. t2.setName("t2");
  65. t1.start();
  66. t2.start();
  67. }
  68. private static void p2() {
  69. final Thread t1 = new Thread(new Runnable() {
  70. @Override
  71. public void run() {
  72. SynchronizedDemo.test2();
  73. }
  74. });
  75. final Thread t2 = new Thread(new Runnable() {
  76. @Override
  77. public void run() {
  78. SynchronizedDemo.test2();
  79. }
  80. });
  81. t1.setName("t1");
  82. t2.setName("t2");
  83. t1.start();
  84. t2.start();
  85. }
  86. public static void p1(){
  87. final SynchronizedDemo demo = new SynchronizedDemo();
  88. final Thread t1 = new Thread(new Runnable() {
  89. @SneakyThrows
  90. @Override
  91. public void run() {
  92. demo.test0();
  93. }
  94. },"t1");
  95. final Thread t2 = new Thread(new Runnable() {
  96. @Override
  97. public void run() {
  98. demo.test1();
  99. }
  100. },"t2");
  101. t1.start();
  102. t2.start();
  103. }
  104. }
  1. package com.qf.sy2103.thread02;
  2. /**
  3. * 如下代码能否成功同步
  4. */
  5. public class SynchronizedDemo2 {
  6. public synchronized void test1(){
  7. System.out.println("test1 started...");
  8. for (int i = 0; i < 100; i++) {
  9. System.out.println(Thread.currentThread().getName()+":"+i);
  10. }
  11. }
  12. public synchronized void test2(){
  13. System.out.println("test2 started...");
  14. for (int i = 0; i < 100; i++) {
  15. System.out.println(Thread.currentThread().getName()+":"+i);
  16. }
  17. }
  18. public static void main(String[] args) {
  19. final SynchronizedDemo2 d1 = new SynchronizedDemo2();
  20. final SynchronizedDemo2 d2 = new SynchronizedDemo2();
  21. final Thread t1 = new Thread(new Runnable() {
  22. @Override
  23. public void run() {
  24. d1.test1();
  25. }
  26. });
  27. final Thread t2 = new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. d2.test2();
  31. }
  32. });
  33. t1.start();
  34. t2.start();
  35. }
  36. }
  1. package com.qf.sy2103.thread02;
  2. /**
  3. * 如下代码能否成功同步
  4. */
  5. public class SynchronizedDemo2 {
  6. public synchronized void test1(){
  7. System.out.println("test1 started...");
  8. for (int i = 0; i < 100; i++) {
  9. System.out.println(Thread.currentThread().getName()+":"+i);
  10. }
  11. }
  12. public synchronized void test2(){
  13. System.out.println("test2 started...");
  14. for (int i = 0; i < 100; i++) {
  15. System.out.println(Thread.currentThread().getName()+":"+i);
  16. }
  17. }
  18. public synchronized static void test3(){
  19. System.out.println("test3 started...");
  20. for (int i = 0; i < 100; i++) {
  21. System.out.println(Thread.currentThread().getName()+":"+i);
  22. }
  23. }
  24. public static void main(String[] args) {
  25. final SynchronizedDemo2 d1 = new SynchronizedDemo2();
  26. final SynchronizedDemo2 d2 = new SynchronizedDemo2();
  27. final Thread t1 = new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. d1.test3();
  31. }
  32. });
  33. final Thread t2 = new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. d2.test3();
  37. }
  38. });
  39. t1.start();
  40. t2.start();
  41. }
  42. }