java多线程之Object中关于线程的方法
友情提示:因为方法只是用来展示代码的,有些是伪代码。所以创建方法的时候一定要有含义,不能像我一样只是一个字母就代替了。(本文档种方法纯属比较懒)
在java中Object是所有类的父类,所以Object的方法能被所有类使用,在Object中提供了许多关于线程的类。我们来逐一的看一下
1.wait()函数
wait()函数被调用时该线程会被阻塞挂起,除非两种情况可以解除挂起。一.其他线程调用了该共享对象的notify()或者是notifyAll()方法。二.其他线程调用该线程的interrupt()方法,抛出InterruptedException异常。
如果调用wait()方法没有事先获取对象监视器的锁,调用的时候回抛出IllegalMonitorStateException异常
那么怎样获得共享变量的监视器锁呢?
(1)执行synchronized同步代码块时,使用该变量作为参数。synchronized在线程中是同步锁,用来保证资源同步的。后面会有关于锁的内容。
synchronized (共享变量){
//同步内容
}
(2) 调用该对象的方法,并且方法使用synchronized修饰
public synchronized void f(){
//同步内容
}
在我们使用同步锁的时候需要预防虚假唤醒 ,所谓虚假唤醒就是–线程从挂起状态到可运行状态(就是被唤醒),该线程并没有被其他线程调用notify()或者是notifyAll()方法,没有被被中断,没有等待超时就是虚假唤醒。
虽然虚假唤醒很少发生,但是为了避免出现,需要不断的去测试该线程被唤醒的条件是否满足。不满足继续等待。这里有一个生产消费模式,可以查到,后面有时间的话也会写一写的。生产消费模式可以帮助我们更好的理解这里的内容。
synchronized (obj){
while(条件不满足){
//循环内容
}
}
2 wait(long timeout)函数
和wait()方法不同的是该方法设置了一个超时的参数。如果该线程在指定的时间(参数中的时间,注意是毫秒值)内没有被调用notify()或者notifyAll()方法,那么会因为超时而返回。如果参数设置为0,那么和wati()方法没有区别。如果参数为负数,那么会抛出一个IllegalArgumentException异常。
3 notify()
线程调用共享的notify()后,会唤醒该共享线程上通过wait()方法挂起的线程。该共享变量上可能会有多个线程,notity()会随机唤醒等待的线程。被唤醒的线程不能马上进入执行,只有唤醒它的线程释放了共享变量上的监视器锁后才可以返回。被唤醒不一意味着可以马上执行,该线程还需要和其他线程竞争该锁,只有竞争到该锁,才能继续执行。
4 notifyAll()
notifyAll()会唤醒所有该共享变量上由于调用wait系列而被挂起的线程。
下面通过例子来显示一下notify和notifyAll的区别:
public class ThreadTest {
//创建资源 1
private static volatile Object ob=new Object();
public static void main(String[] args) throws InterruptedException {
//创建线程 2
Thread thA=new Thread(new Runnable() {
@Override
public void run() {
synchronized (ob){
System.out.println("线程A获取到锁");
try{
System.out.println("线程A开始等待");
ob.wait();
System.out.println("线程A等待结束");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
});
//创建线程 3
Thread thB=new Thread(new Runnable() {
@Override
public void run() {
synchronized (ob){
System.out.println("线程B获取同步锁");
try {
System.out.println("线程B开始等待");
ob.wait();
System.out.println("线程B结束等待");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//创建线程 4
Thread thC=new Thread(new Runnable() {
@Override
public void run() {
synchronized (ob){
System.out.println("线程C开始释放锁");
ob.notify();
}
}
});
//启动两个等待的线程 5
thA.start();;
thB.start();
//让线程休眠 6
Thread.sleep(1000);
//启动唤醒线程 7
thC.start();
//等待线程结束
thA.join();
thB.join();;
thC.join();
System.out.println("执行结束");
}
}
1.创建一个共享变量,目的是为了让下面的三个线程都是用同一个变量,这样可以保证是共享对象。
2.创建线程A让他调用wait()方法进行等待,等待的锁对象是1创建的。等待方法也是1调用的。
3.创建线程B他的目的和线程A的目的是一样的。
4.创建线程C用来唤醒等待的线程。
5.启动两个线程
6.让线程休眠,目的是为了让线程A和线程B同时进入等到状态
7.启动线程C,唤醒休眠的线程。
当线程C的唤醒方法为notify()的时候执行结果如下:
从结果中可以看出线程A已经执行完成,而线程B还处于等待状态。是因为,线程A和线程B同时调用了ob的wait()方法,所以被挂起。线程C前面的休眠就是为了让线程A和线程B同时进入等待状态。A和B同时进入等待状态后,调用C。C线程执行了唤醒操作,这里面唤醒了线程A(注意不是任何时候都是唤醒的A,因为线程是抢占式的,所以不确定唤醒谁。),所以A线程执行完毕,而B线程会陷入等待,直到下一次有其他线程唤醒。
当线程使用notifyAll()的时候,执行结果如下:
从执行结果可以等值notityAll()方法唤醒了全部线程,执行结果中B先获取到了资源所以会先执行完成,在B执行完成后线程A开始执行,线程A结束后,主线程开始执行。
注意:notifyAll()只能唤醒该方法前通过wait()挂起的线程,在notifyAll()后面挂起的线程不会被唤醒。