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.IllegalMonitorStateException
synchronized (lock){ // main 线程去申请锁,锁是lock对象
// 创建一个线程 , 申请获取同一把锁
new Thread(new Runnable() {
@Override
public 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()+":获取到到了锁");
// 做完本线程的任务之后,想要释放锁,让其他线程有机会那到这个锁,就可以wait
lock.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);
}
}
}
// 锁对象是什么呢? this
public 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() {
@Override
public void run() {
SynchronizedDemo.test2();
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public 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() {
@Override
public void run() {
SynchronizedDemo.test2();
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public 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
@Override
public void run() {
demo.test0();
}
},"t1");
final Thread t2 = new Thread(new Runnable() {
@Override
public 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() {
@Override
public void run() {
d1.test1();
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public 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() {
@Override
public void run() {
d1.test3();
}
});
final Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
d2.test3();
}
});
t1.start();
t2.start();
}
}