线程基本介绍

一、线程的基本状态

各种状态一目了然,值得一提的是"blocked"这个状态:
线程在Running的过程中可能会遇到阻塞(Blocked)情况

  1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
  2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
  3. 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。

此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread类中的yield方法可以让一个running状态的线程转入runnable。

二、synchronized, wait, notify 是任何对象都具有的同步工具。

  wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另一个对象的wait/notify,因为不同对象的监视器不同,同样会抛出此异常。

synchronized单独使用:

  • 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
    public class Thread1 implements Runnable {
       Object lock;
       public void run() {  
           synchronized(lock){
             ..do something
           }
       }
    }
  • 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
    public class Thread1 implements Runnable {
       public synchronized void run() {  
            ..do something
       }
    }
  •  synchronized, wait, notify结合:典型场景生产者消费者问题
    /**
       * 生产者生产出来的产品交给店员
       */
      public synchronized void produce()
      {
          if(this.product >= MAX_PRODUCT)
          {
              try
              {
                  wait();  
                  System.out.println("产品已满,请稍候再生产");
              }
              catch(InterruptedException e)
              {
                  e.printStackTrace();
              }
              return;
          }
    
          this.product++;
          System.out.println("生产者生产第" + this.product + "个产品.");
          notifyAll();   //通知等待区的消费者可以取出产品了
      }
    
      /**
       * 消费者从店员取产品
       */
      public synchronized void consume()
      {
          if(this.product <= MIN_PRODUCT)
          {
              try 
              {
                  wait(); 
                  System.out.println("缺货,稍候再取");
              } 
              catch (InterruptedException e) 
              {
                  e.printStackTrace();
              }
              return;
          }
    
          System.out.println("消费者取走了第" + this.product + "个产品.");
          this.product--;
          notifyAll();   //通知等待去的生产者可以生产产品了
      }

 三、线程的两种创建方式

  1.通过继承Thread类

package com.wc.study.thread.demo1;
 
public class CreateThreadByExtends extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        Thread thread1 = new CreateThreadByExtends();
        Thread thread2 = new CreateThreadByExtends();
        thread1.start();
        thread2.start();
    }
}

  2.通过实现Runnable接口

package com.wc.study.thread.demo1;
 
public class CreateThreadByRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        Thread thread1 = new Thread(new CreateThreadByRunnable());
        Thread thread2 = new Thread(new CreateThreadByRunnable());
        thread1.start();
        thread2.start();
    }
}

两者的区别:

  1. Runnable的实现方式是实现其接口即可
  2. Thread的实现方式是继承其类
  3. Runnable接口支持多继承,但基本上用不到
  4. Thread实现了Runnable接口并进行了扩展,而Thread和Runnable的实质是实现的关系,不是同类东西,所以Runnable或Thread本身没有可比性。(常说的Runnable更容易资源共享,其实是开两个Thread去运行一个Runnable)
posted @ 2020-12-02 17:25  xujf  Views(116)  Comments(0Edit  收藏  举报