记得以前初学Java时老是搞不清这几个方法,现在稍微记录一下。
概述
sleep是Thread这个类的一个静态方法,调用时则当前线程
睡眠多长时间;
wait是Object类的一个方法,某个对象调用wait方法时,当前线程
等待多长时间。某个对象调用wait/notify/notifyAll方法前,必须获得这个对象的锁,否则会抛出java.lang.IllegalMonitorStateException
异常。
sleep与wait
sleep与wait方法的最大不同就是线程进行同步时,sleep不会释放锁,而wait会。
Demo1
public class Main {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Task task = new Task();
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
}
private static class Task implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
print();
}
private void print() {
System.out.println(Thread.currentThread().toString() + " "
+ System.currentTimeMillis());
}
}
}
这个结果会是怎样呢?
Thread[Thread-0,5,main] 1446863651674
Thread[Thread-2,5,main] 1446863651675
Thread[Thread-1,5,main] 1446863651675
三条线程同时完成。当主线程启动三条线程时,三条线程都睡眠2秒,最终也一起结束睡眠,退出run方法,线程终结。
Demo2
我们将睡眠方法外面加一个对象锁
synchronized (this) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
打印结果:
Thread[Thread-0,5,main] 1446863850163
Thread[Thread-2,5,main] 1446863852163
Thread[Thread-1,5,main] 1446863854163
三条线程隔2秒完成。当主线程启动三条线程时,首先某一条线程获得了对象锁,其他线程进入同步阻塞
状态,这条线程睡眠2秒,睡醒后释放对象锁,退出run方法,第一条线程终结。第二条线程在第一条线程释放对象锁后进行同样的步骤。
Demo3
我们将Thread.sleep(2000)方法改为this.wait(2000)
synchronized (this) {
try {
this.wait(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
打印结果:
Thread[Thread-2,5,main] 1446864186704
Thread[Thread-1,5,main] 1446864186705
Thread[Thread-0,5,main] 1446864186704
三条线程同时完成。当主线程启动三条线程时,首先某一条线程获得了对象锁,其他线程进入同步阻塞状态,这个对象锁调用wait(2000)方法,当前线程释放锁
并等待2秒,等待完毕后退出run方法,线程终结。第二条线程在第一条线程释放对象锁后进行同样的步骤。
通过以上三个Demo可以知道,sleep和wait的最大区别就是sleep不会释放对象锁,而wait会。
notify与notifyAll
其实这两个还是很容易理解的,一个是随机唤醒某一条线程,另外一个是唤醒所有线程。
Demo1
public class Main {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Task task = new Task();
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
Thread.sleep(1); // 保证三条线程都已经调用了wait
synchronized (Task.class) {
Task.class.notify();
}
}
private static class Task implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (Task.class) {
try {
Task.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
print();
}
private void print() {
System.out.println(Thread.currentThread().toString() + " "
+ System.currentTimeMillis());
}
}
}
打印结果:
Thread[Thread-0,5,main] 1446864797683
只有一条线程被唤醒,另外两条线程仍然处于等待状态,所以程序并未结束。
Demo2
将主线程的唤醒方法改为notifyAll
synchronized (Task.class) {
Task.class.notifyAll();
}
打印结果:
Thread[Thread-1,5,main] 1446864969393
Thread[Thread-2,5,main] 1446864969393
Thread[Thread-0,5,main] 1446864969393
三条线程都被唤醒了。
某个对象调用wait/notify/notifyAll方法前,必须获得这个对象的锁,否则会抛出java.lang.IllegalMonitorStateException
异常。如果我们将对象锁换为this,则调用wait时也需要this对象。
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
调用notify/notifyAll时同样需要代表当前对象是task引用。
Task task = new Task();
......
synchronized (task) {
task.notifyAll();
}
synchronized方法的对象锁
synchronized方法的对象锁是当前对象,即this。
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Task task = new Task();
new Thread(task).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
task.sleep1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
task.sleep2();
}
}).start();
}
private static class Task implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (this) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
print();
}
private void print() {
System.out.println(Thread.currentThread().toString() + " "
+ System.currentTimeMillis());
}
private synchronized void sleep1() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
print();
}
private synchronized void sleep2() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
print();
}
}
}
打印结果:
Thread[Thread-0,5,main] 1446865583845
Thread[Thread-2,5,main] 1446865585844
Thread[Thread-1,5,main] 1446865587844
可以看到三条线程是隔2秒完成的,线程同步的效果出来了,也说明synchronized方法的对象锁为this。如果将run方法同步块的对象锁this改为Task.class呢?