JavaThread多线程开发中Object类详细讲解

 

方法概览

Thread

 

wait notify notifyAll方法详解

作用

阻塞阶段

使用了wait方法之后,线程就会进入阻塞阶段,只有发生以下四种情况中的其中一个,线程才会被唤醒

  • 另一个线程调用了这个线程的notify方法,刚好唤醒的是本线程
  • 另一个线程调用了这个对象的notifyAll方法
  • 过了wait规定的超时时间
  • 线程调用了interrupt

唤醒阶段

notify会唤醒单个处于阻塞状态的线程,唤醒的线程是随机的

notify和wait都需要写在synchronized代码块里,不然会抛出异常

notifyAll会唤醒所有等待的线程

遇到中断

执行wait方法之后,被中断,会抛出InterruptedException这个异常

代码展示

  • 展示wait和notify的基本用法
  • 该代码执行wait方法之后会释放锁,然后thread2执行notify方法
  • notify方法执行完毕之后,并没有立即释放锁,而是接着执行之后的代码,也就是打印“Thread2调用notify”这句话
  • thread2执行完毕之后,会进行释放锁,thread1才会继续执行
  • 在此期间,thread1虽然被唤醒,但是一直在等待thread2同步代码块里面的代码执行完毕
public class Wait {
  public static void main(String[] args) throws InterruptedException {
      Thread1 thread1 = new Thread1();
      Thread2 thread2 = new Thread2();
      thread1.start();
      Thread.sleep(200);
      thread2.start();
  }
  public static Object object = new Object();
  static class Thread1 extends Thread {
      @Override
      public void run() {
          synchronized (object) {
              System.out.println("Thread1执行");
              try {
                  object.wait();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("Thread1获取锁");
          }
      }
  }
  static class Thread2 extends Thread {
      @Override
      public void run() {
          synchronized (object) {
              object.notify();
              System.out.println("Thread2调用notify");
          }
      }
  }
}
/*
Thread1执行
Thread2调用notify
Thread1获取锁
* */
  • notify和notifyAll的展示
  • 第一个输出:threadc调用notifyAll
  • 第二个输出:threadc调用notify
  • 调用notify的时候,程序并没有结束,threadb陷入等待
public class notifyOrAll implements Runnable{
  private static final Object a = new Object();
  public static void main(String[] args) throws InterruptedException {
      Runnable r = new notifyOrAll();
      Thread threada = new Thread(r);
      Thread threadb = new Thread(r);
      Thread threadc = new Thread(new Runnable() {
          @Override
          public void run() {
              synchronized (a) {
//                    a.notifyAll();
                  a.notify();
                  System.out.println(Thread.currentThread().getName() + "notify");
              }
          }
      });
      threada.start();
      Thread.sleep(200);
      threadb.start();
      Thread.sleep(200);
      threadc.start();
  }
  @Override
  public void run() {
      synchronized (a) {
          System.out.println(Thread.currentThread().getName() + "得到锁");
          try {
              System.out.println(Thread.currentThread().getName() + "wait");
              a.wait();
              System.out.println(Thread.currentThread().getName() + "wait结束");
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }
}
/*
Thread-0得到锁
Thread-0wait
Thread-1得到锁
Thread-1wait
Thread-2notifyAll
Thread-1wait结束
Thread-0wait结束
* */
/*
Thread-0得到锁
Thread-0wait
Thread-1得到锁
Thread-1wait
Thread-2notify
Thread-0wait结束
* */
  • 只释放当前monitor
  • 证明wait只释放当前的那把锁
public class OwnMonitor {
  private static volatile Object a = new Object();
  private static volatile Object b = new Object();
  public static void main(String[] args) throws InterruptedException {
      Thread threadA = new Thread(new Runnable() {
          @Override
          public void run() {
              synchronized (a) {
                  System.out.println("threadA得到a");
                  synchronized (b) {
                      System.out.println("threadA得到锁b");

                      try {
                          System.out.println("threadA释放a");
                          a.wait();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      });
      Thread threadB = new Thread(new Runnable() {
          @Override
          public void run() {
              synchronized (a) {
                  System.out.println("threadB得到a");
                  System.out.println("threadB要获取b");
                  synchronized (b) {
                      System.out.println("threadB得到b");
                  }
              }
          }
      });
      threadA.start();
      Thread.sleep(1000);
      threadB.start();
  }
}
/*
threadA得到a
threadA得到锁b
threadA释放a
threadB得到a
threadB要获取b
* */

特点

  • 执行这些方法必须先获取锁
  • notify只能换取一个,而且是随机的
  • 都属于Object。任何对象都可以调用
  • 都是native final修饰的

当线程从wait状态刚被唤醒时,通常不能直接得到锁,那就会从waiting状态转换到blocked状态,抢到锁之后状态转变为runnable

如果发生异常,则直接跳到Terminated状态

通过wait notify方法实现生产者和消费者

  • 将storge当作生产者和消费者进行工作的仓库
  • 如果storge中没有数据,生产者就开始wait
  • 如果storge中数据满了,消费者就开始wait
  • 生产者和消费者每进行一次生产和消费,就执行notify
public class ProducerConsumer {
  public static void main(String[] args) {
      Storge storge = new Storge();
      Producer producer = new Producer(storge);
      Consumer consumer = new Consumer(storge);
      new Thread(producer).start();
      new Thread(consumer).start();
  }
}
class Producer implements Runnable {
  private Storge storge;
  public Producer(Storge storge) {
      this.storge = storge;
  }
  @Override
  public void run() {
      for (int i = 0; i < 100; i++) {
          storge.put();
      }
  }
}
class Consumer implements Runnable {
  private Storge storge;
  public Consumer(Storge storge) {
      this.storge = storge;
  }
  @Override
  public void run() {
      for (int i = 0; i < 100; i++) {
          storge.take();
      }
  }
}
class Storge {
  private int maxSize;
  private LinkedList<Date> storge;
  public Storge() {
      maxSize = 10;
      storge = new LinkedList<>();
  }
  public synchronized void put() {
      while (storge.size() == maxSize) {
          try {
              wait();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      storge.add(new Date());
      System.out.println("已经有了" + storge.size());
      notify();
  }
  public synchronized void take() {
      while (storge.size() == 0) {
          try {
              wait();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      System.out.println("拿到了" + storge.poll() + "还剩" + storge.size());
      notify();
  }
}

 

sleep方法详解

作用:让线程在预期的时间执行,其他时间不占用CPU资源

特点:和wait不一样,sleep不释放锁

sleep不会释放锁

证明sleep不会释放 synchronized锁

public class SleepSyn implements Runnable{
  public static void main(String[] args) {
      SleepSyn sleepSyn = new SleepSyn();
      new Thread(sleepSyn).start();
      new Thread(sleepSyn).start();
  }
  @Override
  public void run() {
      syn();
  }
  private synchronized void syn() {
      System.out.println(Thread.currentThread().getName() + "获取锁");
      try {
          Thread.sleep(2000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "释放锁");
  }
}
/*
* Thread-0获取锁
Thread-0释放锁
Thread-1获取锁
Thread-1释放锁
* */

证明sleep不释放Lock锁

public class sleepLock implements Runnable{
  private static final Lock LOCK = new ReentrantLock();
  @Override
  public void run() {
      LOCK.lock();
      System.out.println(Thread.currentThread().getName() + "获取锁");
      try {
          Thread.sleep(2000);
      } catch (InterruptedException e) {
          e.printStackTrace();
      } finally {
          LOCK.unlock();
      }
      System.out.println(Thread.currentThread().getName() + "释放锁");
  }
  public static void main(String[] args) {
      sleepLock sleepLock = new sleepLock();
      new Thread(sleepLock).start();
      new Thread(sleepLock).start();
  }
}
/*
* Thread-0获取锁
Thread-0释放锁
Thread-1获取锁
Thread-1释放锁
* */

sleep响应中断

  • 抛出InterruptedException
  • 会清除中断状态
  • 中断之后,抛出异常继续执行
public class sleepInterrupted implements Runnable{
  public static void main(String[] args) throws InterruptedException {
      Thread thread = new Thread(new sleepInterrupted());
      thread.start();
      Thread.sleep(2000);
      thread.interrupt();
  }
  @Override
  public void run() {
      for (int i = 0; i < 10; i++) {
          System.out.println(new Date());
          try {
              TimeUnit.SECONDS.sleep(1);
          } catch (InterruptedException e) {
              System.out.println("中断");
              e.printStackTrace();
          }
      }
  }
}
/*
* Fri Jan 27 21:11:57 CST 2023
Fri Jan 27 21:11:58 CST 2023
中断
Fri Jan 27 21:11:59 CST 2023
java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at java.lang.Thread.sleep(Thread.java:340)
 at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
 at com.jx.JavaTest.ThreadObjectMethod.sleepInterrupted.run(sleepInterrupted.java:21)
 at java.lang.Thread.run(Thread.java:748)
Fri Jan 27 21:12:00 CST 2023
Fri Jan 27 21:12:01 CST 2023
Fri Jan 27 21:12:02 CST 2023
Fri Jan 27 21:12:03 CST 2023
Fri Jan 27 21:12:04 CST 2023
Fri Jan 27 21:12:05 CST 2023
Fri Jan 27 21:12:06 CST 2023
Process finished with exit code 0
* */

总结

sleep方法可以让线程进入waiting状态,不占用CPU资源,但是不释放锁,规定时间之后再运行

休眠期间如果被打断,会抛出异常并清除中断状态

 

join方法详解

新线程加入,主线程等子线程执行完毕

代码展示

  • 前一个结果是使用join
  • 后一个结果是没使用join
  • 可知使用join之后,主线程会等join的线程执行完毕再继续执行
public class join {
  public static void main(String[] args) throws InterruptedException {
      Thread thread1 = new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName() + "执行完毕");
          }
      });
      Thread thread2 = new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName() + "执行完毕");
          }
      });
      thread1.start();
      thread2.start();
      System.out.println("开始等待子线程运行");
//        thread1.join();
//        thread2.join();
      System.out.println("所有线程执行完毕");
  }
}
/*
* 开始等待子线程运行
Thread-0执行完毕
Thread-1执行完毕
所有线程执行完毕
* */
/*
* 开始等待子线程运行
所有线程执行完毕
Thread-1执行完毕
Thread-0执行完毕
* */
  • 遇到中断
  • 第一个的运行结果是主线程没中断的打印结果
  • 第二个的运行结果是join期间进行中断的打印结果,可知在打印了“子线程运行完毕”之后,依然打印了“启动”两个字,可知会造成运行混乱
  • 可以在捕获异常的代码块中,将join的线程也中断,可以解决上面的问题
public class joinInterrupt {
  public static void main(String[] args) {
      Thread main1 = Thread.currentThread();
      Thread thread1 = new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  main1.interrupt();
                  Thread.sleep(2000);
                  System.out.println("启动");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      });
      thread1.start();
      System.out.println("join");
      try {
          thread1.join();
      } catch (InterruptedException e) {
          System.out.println(Thread.currentThread().getName() + "中断");
          // thread1.interrupt();
          e.printStackTrace();
      }
      System.out.println("子线程运行完毕");
  }
}
/*
* join
启动
子线程运行完毕
* */
/*
* join
main中断
子线程运行完毕
java.lang.InterruptedException
 at java.lang.Object.wait(Native Method)
 at java.lang.Thread.join(Thread.java:1252)
 at java.lang.Thread.join(Thread.java:1326)
 at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23)
启动
Process finished with exit code 0
* */
/*
* join
main中断
子线程运行完毕
java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt$1.run(joinInterrupt.java:13)
 at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException
 at java.lang.Object.wait(Native Method)
 at java.lang.Thread.join(Thread.java:1252)
 at java.lang.Thread.join(Thread.java:1326)
 at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23)
Process finished with exit code 0
* */

join期间,线程处于WAITING状态

public class joinStates {
  public static void main(String[] args) throws InterruptedException {
      Thread main1 = Thread.currentThread();
      Thread thread = new Thread(new Runnable() {
          @Override
          public void run() {
              try {
                  Thread.sleep(3000);
                  System.out.println(main1.getState());
                  System.out.println("子线程运行结束");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      });
      thread.start();
      System.out.println("join");
      thread.join();
      System.out.println("运行完毕");
  }
}
/*
* join
WAITING
子线程运行结束
运行完毕
* */

 

yield方法

用来释放CPU时间片,但是不一定能达到预期的效果,因为有时CPU资源不紧张,无需yield

和sleep的区别是:sleep期间不会被再次调度但是yield会立刻处于竞争状态,还会随时再次被调度

关于Java Thread多线程开发中Object类详细讲解的文章就介绍至此,更多相关Java Object类内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 背景公司的一个服务需要做类似于分片的逻辑,一开始服务基于传统部署方式通过本地配置文件配置的方式就可以指定该机器服务的分片内容如:0,1,2,3,随着系统的升级迭代,该服务 ...