目录
sleep()/wait()/yield()/join()对比
线程常用的方法之sleep()
- sleep() 方法的作用是让当前线程暂停指定的时间(毫秒)
- sleep()方法只是暂时让出cpu执行权,并不释放锁
- 由于对象锁没有被释放,其他线程仍然无法访问这个对象
- sleep()方法不需要再同步的代码块中执行,wait()方法必须在同步代码中执行
- sleep()可以通过interrupt()方法打断线程的暂停状态
- sleep()只是线程的操作,用于短暂暂停线程,不涉及线程间通信
- sleep()是Thread类的方法
sleep与wait对比
public class SleepTest {
/**
* sleep()方法不会释放锁,因此线程是按照先后顺序执行的
*/
public synchronized void sleepMethod(){
System.out.println("Sleep start : " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Sleep end : "
+ Thread.currentThread().getName());
}
/**
* wait()方法会释放锁,因此一旦调用wait()方法就会造成其他线程运行
*/
public synchronized void waitMethod(){
System.out.println("Wait start : " + Thread.currentThread().getName());
//要调用wait方法必须要在同步代码块中调用
//释放锁的前提时先拿到锁
synchronized (this){
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Wait end :" + Thread.currentThread().getName());
}
public static void main(String[] args) {
final SleepTest test1 = new SleepTest();
//new五个线程调用sleep方法验证是否释放锁
for(int i = 0; i < 5; i++){
new Thread(() -> test1.sleepMethod()).start();
}
try {
//暂停十秒,等上面程序执行完成
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----sleep执行完毕 分割线 wait方法开始执行-----");
final SleepTest test2 = new SleepTest();
for(int i = 0; i < 5; i++){
new Thread(() -> test2.waitMethod()).start();
}
}
}
wait()方法
- wait() / notify()方法通常成对出现
- wait() / notify()方法需要获取对象锁方可调用
- wait() / motify() 方法要写下同步代码块或者同步方法内
- 一旦调用wait() 其他线程将可以访问这个对象
- 当一个线程执行到wait() 方法时,他就进入到一个和该对象相关的等待池中,同时失去了对象的锁,可以允许其他线程执行一些同步操作
- wait() 可以通过interrupt 方法打断线程的暂停状态,从而使线程立刻抛出中断异常.
- wait() notify() 是Object类的方法
- 重获对象锁:
- a)设置wait() 方法的参数,如wait(1000)表明借出去1秒之后自动回收锁
- b)让解锁的线程通知(notify)当前线程,借锁线程用完了当前线程马上回收回来
notify()/notifyAll()方法
- notify()唤醒在此对象监视器(对象锁)上等待的单个线程
- 当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中
- 该线程将等待从锁池中获得锁,然后回到wait()前的线程
- notifyAll()唤醒在此对象监视器(对象锁)上等待的所有线程
- 唤醒的线程的顺序——LIFO(Last In First Out,后进先出)
- wait()/notify()/notifyAll() 涉及线程间的通信
public class WaitClassDemo {
private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
public static void main(String[] args) {
Object obj = new Object();
for (int i = 0; i < 5; i++) {
new WaitThread(i + "", obj).start();
}
new NotifyThread(obj).start();
}
/**
* 调用wait()方法的线程
*/
static class WaitThread extends Thread {
Object obj;
public WaitThread(String name, Object obj) {
setName("WaitThread" + name);
this.obj = obj;
}
@Override
public void run() {
synchronized (obj) {
System.out.println(sdf.format(new Date()) + " " + getName() + " before wait()");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sdf.format(new Date()) + " " + getName() + " after wait()");
}
}
}
/**
* 调用notify()/notifyAll()
*/
static class NotifyThread extends Thread {
Object obj;
public NotifyThread(Object obj) {
setName("NotifyThread");
this.obj = obj;
}
@Override
public void run() {
synchronized (obj) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sdf.format(new Date()) + " NotifyThread before notify()");
// 唤醒所有线程 用notifyAll()会按照后进先出(LIFO)的原则恢复线程
obj.notifyAll();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sdf.format(new Date()) + " NotifyThread after notify()");
}
}
}
}
yield()方法
- yield()方法的意思是告诉CPU执行其他的线程,当前线程让出CPU的执行权利
- yield()方法不能保证使得当前正在运行的线程迅速转换到可运行的状态
- yield()方法的作用就是将当前线程从执行中的状态转变到可执行状态
- yield()方法不能保证其他线程一定能够执行,因为执行过yield()方法的 线程当前依然是可执行的状态,有可能被cpu再次执行
- 执行yield()方法的线程不会释放锁
join()方法
- join()方法可以使得一个线程在另一个线程结束后再执行
- 如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程执行完成了
- join()方法可以设定超时时间,使得join()方法的影响在超时后无效
- join()重载方法有:join(long millis),join(long millis, int nanos)
- 当join()超时后,主方法和任务线程申请运行的时候是平等的
- join()可以抛出InterruptedException对中断做出回应
//join让出执行权限,等主程序执行完毕后执行
public class JoinDemo {
public static void main(String[] args) throws InterruptedException
{
Thread thread2
= new Thread(() -> {
System.out.println("thread2 started");
System.out.println("thread2 sleeping for 2 seconds");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 completed");
});
Thread thread1 = new Thread(() -> {
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 started");
System.out.println("thread1 sleeping for 2 seconds");
try{
Thread.sleep(2000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("thread1 completed");
});
// 线程1启动
thread1.start();
// 线程1调用join()
// 问题:线程1和线程2的运行先后
// thread1.join();
thread2.start();
}
}
sleep()/wait()/yield()/join()对比
- Thread.sleep(long) 和Thread.yield()都是Thread类的静态方法,join()是由线程对象来调用
- sleep()使线程从运行状态进入阻塞状态,超时后进入就绪态, 是否进入运行态就要看操作系统的内部调用机制
- 如果Thread.sleep()/Thread.yield()使用在由synchronized锁定的代码块中, 那么在调用他们的过程当中,并不会释放锁。wait()方法会释放锁。
- join()方法可以使得一个线程在另一个线程结束后再执行
- yield()方法是让当前线程由运行状态进入就绪状态,然后让操作系统重新调度一次, 这次调度只会让处于就绪队列中比当前线程优先级高或者相等的线程运行, 很可能某个线程在调用了yield方法后,又被操作系统调度进来运行
- wait()和notify()、notifyAll() 这三个方法用于协调多个线程对共享数据的存取, 必须在synchronized语句块内使用这三个方法,否则可能会抛出 (MonitorStateIlegalException,锁状态异常)