一、sleep

  • Thread类的静态方法
  • 当前线程由RUNNABLE状态的会进入TIMED_WAITING(超时等待);
  • 超时等待过程中线程持有的锁并不会释放;
1、API
Thread.sleep(long millis); // 单位:毫秒
Thread.sleep(long timeout, int nanos); // 毫秒,纳秒
或
TimeUnit.SECONDS.sleep(long timeout); // 这个时间单位可以优选,底层调用的其实还是Thread.sleep(ms, ns);
2、示例
public class ThreadOperation {
	static Object lock = new Object();
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					System.out.println("已进入线程 " + Thread.currentThread().getName());
					try {
						Thread.sleep(1000); // 睡会,切换到主线程,看看主线程能不能拿到锁
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(Thread.currentThread().getName() + " is end");
			}
		});
		thread.start();
		try {
			Thread.sleep(1000); // 睡会,保证进入子线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("哈哈哈");
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " is end");
		}
	}

结果:可以看到,子线程超时等待时并不会释放锁

已进入线程 Thread-0
哈哈哈
Thread-0 is end
main is end

二、wait

  • Object类的方法,使用时必须是已经获得锁(lock.wait())
  • 当前线程由RUNNABLE状态的会进入WAITING或TIMED_WAITING(超时等待)状态;
  • 等待过程会释放锁资源,直到被唤醒(notify()/notifyAll())或是超时结束后会再去争夺资源
1、API
wait(); // 需要使用notify()或notifyAll()唤醒等待该锁的线程
wait(long timeout); // 毫秒,等待一定的时间
wait(long timeout, int nanos); // 毫秒,纳秒
2、示例1

wait(long timeout);

public class ThreadOperation {
	static Object lock = new Object();
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					System.out.println("已进入线程 " + Thread.currentThread().getName());
					try {
						lock.wait(1000); // 此线程释放锁加入等待队列, 1s后又争夺资源
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " is end");
				}
			}
		});
		thread.start();
		try {
			Thread.sleep(300); // 睡会,保证进入子线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("哈哈哈");
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " is end");
		}
	}
}

结果:可以看到,子线程超时等待时释放了锁

已进入线程 Thread-0
哈哈哈
main is end
Thread-0 is end
3、示例2

监视器wait()后当前线程进入WAITTING状态
wait() 配合notify() 或notifyAll()使用,详见另一blognotify() 和 notifyAll()方法的使用和区别

三、stop(已弃用)

  • stop()调用后finally中的代码还会执行,当前线程立即终止并释放一切资源;
  • 这就会不安全,因为它在终止一个线程时会强制中断线程的执行,不管run方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象。这一现象会被其它因为请求锁而阻塞的线程看到,使他们继续向下执行。这就会造成数据的不一致。
  • 例如:从A账户向B账户转账500元,这一过程分为三步,第一步是从A账户中减去500元,假如到这时线程就被stop了,那么这个线程就会释放它所取得锁,然后其他的线程继续执行,这样A账户就莫名其妙的少了500元而B账户也没有收到钱。这就是stop方法的不安全性
public class ThreadDemo implements Runnable{
	static Object lock = new Object();

	public static void main(String[] args) {
		Thread thread1 = new Thread(new ThreadDemo(), "thread-a");
		Thread thread2 = new Thread(new ThreadDemo(), "thread-b");
		thread1.start();
		thread2.start();
		try {
			TimeUnit.SECONDS.sleep(1); // 睡会,让走到子线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " 获得了锁");
			try {
				Thread.currentThread().stop();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				System.out.println(Thread.currentThread().getName() + " stop()");
				System.out.println("唤醒所有持有lock锁的线程");
				lock.notifyAll();
			}
		}
		System.out.println(Thread.currentThread().getName() + " end"); // 因为被强行终止,所以不会执行这行代码
	}

	@Override
	public void run() {
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " 获得了锁");
			try {
				System.out.println(Thread.currentThread().getName() + " wait()");
				lock.wait();
				System.out.println(Thread.currentThread().getName() + " end");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

结果:可以看到主线程被强行终止后,finally中代码还会执行,但不会再往下走;锁被释放后被唤醒持有该锁的所有子线程又继续获得锁走下去

thread-a 获得了锁
thread-a wait()
thread-b 获得了锁
thread-b wait()
main 获得了锁
main stop()
唤醒所有持有lock锁的线程
thread-b end
thread-a end

四、suspend(已弃用)

  • 当前线程调用suspend()后后立即挂起,线程状态还是RUNNABLE,不会释放锁资源,这就会很容导致死锁(这也是弃用它的原因);
  • 实质是由RUNNING运行中转为RUNNABLE(可运行,等待线程调度再次给到cpu资源),只不过Thread的统称这两种都为RUNNABLE;
public class ThreadDemo implements Runnable{
	static Object lock = new Object();
	public static void main(String[] args) {
		Thread thread = new Thread(new ThreadDemo());
		thread.start();
		try {
			TimeUnit.SECONDS.sleep(1); // 睡会,让走到子线程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("resume前" + thread.getName() + "状态是 " + thread.getState()); // 2
		thread.resume();
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " 获得了锁"); // 4
		}
		System.out.println(Thread.currentThread().getName() + " end"); // 5
	}

	@Override
	public void run() {
		synchronized (lock) {
			System.out.println(Thread.currentThread().getName() + " 获得了锁"); // 1
			Thread.currentThread().suspend();
			System.out.println(Thread.currentThread().getName() + " end"); // 3
		}
	}
}

结果

Thread-0 获得了锁
resume前Thread-0状态是 RUNNABLE
Thread-0 end
main 获得了锁
main end
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐