Object和Thread一些和线程阻塞相关的常用方法

Object中的方法

每一个对象的对象头中,会有一个锁标志位。可以通过获取和释放对象锁的方式,来达到阻塞线程的目的。通过synchronized关键字可以获取到对象的锁。

wait()

wait()方法会释放对象锁。如果要使用wait()方法,就必须先获取到对象的锁。所以wait()方法必须在synchronize修饰的方法或同步代码块中使用。

// Object.wait()方法使用
public class ObjectWait implements Runnable{

  public static void main(String[] args) {
      Thread thread = new Thread(new ObjectWait());
      thread.start();
  }

  @Override
  public void run() {
      synchronized (this) {
          try {
              System.out.println("已经获取到对象的锁");
              System.out.println("开始调用对象的wait方法");
              this.wait();
              System.out.println("线程执行结束");
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }
}

执行结果

已经获取到对象的锁
开始调用对象的wait方法

"线程执行结束"这句没有打印,说明线程没有执行完成。因为wait()不加时间参数表示无限等待。要如何唤醒无限等待的线程呢?这就需要用到下面两个方法:notify()和notifyAll()

notify()和notifyAll()

notify()和notifyAll()都可以唤醒调用wait()方法等待的线程,它们区别是:notify()只会随机唤醒一个等待的线程,notifyAll()会唤醒所有等待的线程。下面我们用代码演示下这两者的区别。
notify()方法:

package theadcoreknowledge.objectandthreadcommonmethod;

// 使用notify随机唤醒一个等待的线程
public class NotifyOneWaitThread implements Runnable {

    public static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        NotifyOneWaitThread notifyOneWaitThread = new NotifyOneWaitThread();
        Thread thread = new Thread(notifyOneWaitThread);
        Thread thread2 = new Thread(notifyOneWaitThread);
        thread.start();
        thread2.start();
        Thread.sleep(100);
        synchronized (lock) {
            // 随机唤醒一个等待的线程
            lock.notify();
        }
    }

    @Override
    public void run() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName() + "已经获取到对象的锁");
                System.out.println(Thread.currentThread().getName() + "开始调用对象的wait方法");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + "执行完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果:

Thread-0已经获取到对象的锁
Thread-0开始调用对象的wait方法
Thread-1已经获取到对象的锁
Thread-1开始调用对象的wait方法
Thread-0执行完成

从执行结果中可以看到:只打印了“Thread-0执行完成”,而没有打印“Thread-1执行完成”。这说明notify方法只会唤醒一个等待的线程,至于会唤醒哪一个,这个并不是由我们来决定的,而是jvm来决定。不同JDK版本的实现方式会有所不同,JDK8会唤醒等待时间最长的线程。
notifyAll():

// 使用notifyAll()唤醒所有等待的线程
public class NotifyAllWaitThreads implements Runnable{
    public static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        NotifyAllWaitThreads notifyAllWaitThreads = new NotifyAllWaitThreads();
        Thread thread = new Thread(notifyAllWaitThreads);
        Thread thread2 = new Thread(notifyAllWaitThreads);
        thread.start();
        thread2.start();
        Thread.sleep(100);
        synchronized (lock) {
            // 唤醒所有等待的线程
            lock.notifyAll();
        }
    }

    @Override
    public void run() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName() + "已经获取到对象的锁");
                System.out.println(Thread.currentThread().getName() + "开始调用对象的wait方法");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + "执行完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

执行结果:

Thread-0已经获取到对象的锁
Thread-0开始调用对象的wait方法
Thread-1已经获取到对象的锁
Thread-1开始调用对象的wait方法
Thread-1执行完成
Thread-0执行完成

可以看到,Thread-0和Thread-1都执行完成了。说明notifyAll()可以唤醒所有等待的线程。被唤醒的线程谁先优先执行呢?这个也不是由我们来决定的。线程被唤醒后都重新去获取对象锁,最先拿到对象锁的线程会先执行。

Thread中的方法

Thread中也有一些方法可以使线程陷入阻塞状态,如sleep()、join()。

sleep()

sleep方法是必须传参的,而且参数必须是大于等于0的long类型。如果传入的参数为0,线程会放弃cpu的执行权,并重新去获取cpu的执行权。

join()

join()方法有些特殊,在哪个线程中使用了join()方法,就阻塞哪个线程,直到调用join方法的thread执行完成。

// Thread.join()方法演示
public class ThreadJoin implements Runnable {

   public static void main(String[] args) throws InterruptedException {
       ThreadJoin threadJoin = new ThreadJoin();
       Thread thread = new Thread(threadJoin);
       thread.start();
       thread.join();
       System.out.println(Thread.currentThread().getName() + "执行完毕");
   }

   @Override
   public void run() {
       try {
           System.out.println(Thread.currentThread().getName() + "开始执行");
           Thread.sleep(1000);
           System.out.println(Thread.currentThread().getName() + "执行完毕");
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}

执行结果

Thread-0开始执行
Thread-0开始完毕
main开始完毕

在主线程main中使用了thread.join(),这时候主线程就会被阻塞,直到thread线程执行完成后主线程才会继续执行。

join()和wait()之间的关联

Thread的sleep()、yield()方法都是native方法,不是由java实现的,只有join()是由java实现的。

    /**
    * Waits at most {@code millis} milliseconds for this thread to
    * die. A timeout of {@code 0} means to wait forever.
    *
    * <p> This implementation uses a loop of {@code this.wait} calls
    * conditioned on {@code this.isAlive}. As a thread terminates the
    * {@code this.notifyAll} method is invoked. It is recommended that
    * applications not use {@code wait}, {@code notify}, or
    * {@code notifyAll} on {@code Thread} instances.
    *
    * @param  millis
    *         the time to wait in milliseconds
    *
    * @throws  IllegalArgumentException
    *          if the value of {@code millis} is negative
    *
    * @throws  InterruptedException
    *          if any thread has interrupted the current thread. The
    *          <i>interrupted status</i> of the current thread is
    *          cleared when this exception is thrown.
    */
   public final synchronized void join(long millis)
   throws InterruptedException {
       long base = System.currentTimeMillis();
       long now = 0;

       if (millis < 0) {
           throw new IllegalArgumentException("timeout value is negative");
       }

       if (millis == 0) {
           while (isAlive()) {
               wait(0);
           }
       } else {
           while (isAlive()) {
               long delay = millis - now;
               if (delay <= 0) {
                   break;
               }
               wait(delay);
               now = System.currentTimeMillis() - base;
           }
       }
   }

可以看到,join()方法的阻塞线程最终调用的是wait()方法。但是没有调用notify()或者notifyAll()方法。那么为什么调用wait(0)后线程不会一直阻塞呢?那是因为当线程执行完成之后,会自动调用notify()方法。所以我们可以用另外一种方式去实现join()方法。
通过wait()方法实现join()

// 通过wait方式实现join
public class OtherWayImplJoin {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName()+"开始执行");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+"执行完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        synchronized (thread){
            thread.wait();
        }
        System.out.println(Thread.currentThread().getName()+"执行完成");
    }
}

yield()

当前线程让出cpu的执行权,调用此方法不会造成线程阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值