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方法,会抛出如下异常
但是,如果直接调用Thread.sleep方法,是不会有异常的。
/*** wait 和 sleep 的区别**/public class Demo3 {public static void main(String[] args) throws InterruptedException {// sleep 没有代码位置的限制Thread.sleep(1000);// 创建一个对象,并且这个对象就是一把锁Object lock = new Object();// lock.wait(); //Exception in thread "main" java.lang.IllegalMonitorStateExceptionsynchronized (lock){ // main 线程去申请锁,锁是lock对象// 创建一个线程 , 申请获取同一把锁new Thread(new Runnable() {@Overridepublic void run() {// 等待一秒钟,在申请锁try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock){// 模拟业务处理System.out.println(Thread.currentThread().getName()+":获取到了锁");// 唤醒所有在该锁上wait的线程lock.notifyAll();}}},"A").start();// 做一些业务处理System.out.println(Thread.currentThread().getName()+":获取到到了锁");// 做完本线程的任务之后,想要释放锁,让其他线程有机会那到这个锁,就可以waitlock.wait(); // 线程会进入WAITING 状态System.out.println(Thread.currentThread().getName()+":运行结束");}}}
synchronize 关键字
作用:如果某个线程想要进入synchronize关键字修饰的代码块或者方法,那么该线程必须要先抢到synchronize关键字对应的那把锁。相当于同一时刻,加了synchronize关键字的代码块或者方法,只能由一个线程进行执行。
| 使用场景 | 锁是什么 |
|---|---|
| 修饰代码块 | 代码中显式指定一个锁对象 |
| 修饰实例方法 | 锁是实例对象 this |
| 修饰静态方法 | 锁是一个Class对象(类名.class) |
修饰代码块的语法:
// 同步代码块 , 同一时间只允许一个线程进入同步代码块synchronized (锁对象){}
错误代码示范:
public class SyncDemo01 {public void test1(){synchronized (new Object()){for (int i=0;i<20;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}public static void main(String[] args) {final SyncDemo01 syncDemo01 = new SyncDemo01();final Thread threadA = new Thread(() -> syncDemo01.test1(),"A");final Thread threadB = new Thread(() -> syncDemo01.test1(),"B");threadA.start();threadB.start();}}
/*** 当使用 synchronized 修饰实例方法时, 锁是什么? 锁就是this对象*/public synchronized void test1(){}
public synchronized static void test2(){}
package com.qf.thread;public class SyncDemo01 {public Object lock = new Object();public void test1(){synchronized (SyncDemo01.class){for (int i=0;i<20;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}// 锁对象是什么呢? thispublic synchronized void test2(){for (int i=0;i<20;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}// synchronized 修饰静态方法,锁是什么呢? 是当前类的class对象// final Class<SyncDemo01> syncDemo01Class = SyncDemo01.class;// SyncDemo01.class是不是单例? 是的 。 当jvm类加载器加载SyncDemo01的时候,自动创建了该class对象,并且是单例public synchronized static void test3(){for (int i=0;i<20;i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public static void main(String[] args) {final SyncDemo01 o1 = new SyncDemo01();final Thread threadA = new Thread(() -> o1.test1(),"A");final Thread threadB = new Thread(() -> o1.test3(),"B");threadA.start();threadB.start();}public static void p2(){final SyncDemo01 o1 = new SyncDemo01();final Thread threadA = new Thread(() -> o1.test1(),"A");final Thread threadB = new Thread(() -> o1.test2(),"B");threadA.start();threadB.start();}public static void p1(){final SyncDemo01 o1 = new SyncDemo01();final SyncDemo01 o2 = new SyncDemo01();final Thread threadA = new Thread(() -> o1.test1(),"A");final Thread threadB = new Thread(() -> o2.test1(),"B");threadA.start();threadB.start();}}
思考案例:
开启两个线程,执行实例方法m,方法m为循环打印1-20个数字。在不开启同步的情况下,会是什么效果?
如果开启synchronized同步,会是什么效果?
注意:同步的原则,必须在相同的锁对象上进行同步!
思考:使用同步代码块还是使用同步方法呢?
package com.qf.sy2103.thread02;/*** 思考:使用同步代码块还是使用同步方法呢?* 推荐用法:尽可能减少同步代码的范围。在必须要进行同步的地方使用 代码块方式开启同步。*/public class ThreadDemoChoose {public void test1(String path){//1. 读取指定 path的文件 ,并把文件的内容读取出来进行分析//2. 把分析的结果写入到某个指定的文件中synchronized (this){}}}
思考:持有锁的线程如果出现异常,锁是否会被释放?(练习)
/*** synchronize 到底锁的是什么* synchronized 代表的语义是开启一段同步代码块* 同步代码块:一个特殊的代码块,只能有一个线程进入到同步代码块中,其他线程进不来* 为什么线程能进入同步代码块呢? 能进入的线程,一定是申请到了可以进入同步代码块的锁* 锁是什么? 一个java对象,可以是锁。 类.class ,Demo4.class 可以是一把锁。*/package com.qf.sy2103.thread02;import lombok.SneakyThrows;public class SynchronizedDemo {public static void test3(){System.out.println("test3 stated..");synchronized (SynchronizedDemo.class){ // 可以使用 当前实例对象 (this)for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() +":" + i);}}}/*** 锁是什么 ? 类名.class* 当类 SynchronizedDemo 被jvm 加载之后,会在堆内存中创建一个对象,这个对象就是 SynchronizedDemo.class* 是一个 类型为 Class<SynchronizedDemo> 的对象*/public synchronized static void test2(){System.out.println("test2 start..");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() +":" + i);}}public void test0() throws InterruptedException {// final Object lock = new Object();System.out.println("test0 stated..");synchronized (this){ // 可以使用 当前实例对象 (this)for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() +":" + i);}}}/*** 当使用 synchronized 修饰实例方法时, 锁是什么? 锁就是this对象*/public synchronized void test1(){System.out.println("test1 start..");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() +":" + i);}}public static void main(String[] args) {// p1();// p2();final Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {SynchronizedDemo.test2();}});final Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {SynchronizedDemo.test3();}});t1.setName("t1");t2.setName("t2");t1.start();t2.start();}private static void p2() {final Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {SynchronizedDemo.test2();}});final Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {SynchronizedDemo.test2();}});t1.setName("t1");t2.setName("t2");t1.start();t2.start();}public static void p1(){final SynchronizedDemo demo = new SynchronizedDemo();final Thread t1 = new Thread(new Runnable() {@SneakyThrows@Overridepublic void run() {demo.test0();}},"t1");final Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {demo.test1();}},"t2");t1.start();t2.start();}}
package com.qf.sy2103.thread02;/*** 如下代码能否成功同步*/public class SynchronizedDemo2 {public synchronized void test1(){System.out.println("test1 started...");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public synchronized void test2(){System.out.println("test2 started...");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public static void main(String[] args) {final SynchronizedDemo2 d1 = new SynchronizedDemo2();final SynchronizedDemo2 d2 = new SynchronizedDemo2();final Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {d1.test1();}});final Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {d2.test2();}});t1.start();t2.start();}}
package com.qf.sy2103.thread02;/*** 如下代码能否成功同步*/public class SynchronizedDemo2 {public synchronized void test1(){System.out.println("test1 started...");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public synchronized void test2(){System.out.println("test2 started...");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public synchronized static void test3(){System.out.println("test3 started...");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}public static void main(String[] args) {final SynchronizedDemo2 d1 = new SynchronizedDemo2();final SynchronizedDemo2 d2 = new SynchronizedDemo2();final Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {d1.test3();}});final Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {d2.test3();}});t1.start();t2.start();}}
