多线程与并发(一)——概述、线程状态

iwehdio的博客园:https://www.cnblogs.com/iwehdio/

1、多线程概述

  • 多线程:多条执行路径,主线程和子线程并行交替执行。

  • 进程:是执行程序的一次执行过程,是系统资源分配的单位。在操作系统中运行的程序就是进程。

  • 线程:是独立的执行路径,是CPU调度和执行的单位。一个进程可以有多个线程。

  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度。

  • 对于同一份资源操作,会存在资源抢夺的问题,需要加入并发控制。

  • 线程创建:

    • 三种方法:继承Thread类、实现Runnable接口或实现Callable接口。

    • 继承Thread类:

      • Thread类实现了Runnable接口。

      • 自定义线程类继承Thread类,重写run()方法,编写线程 执行体。创建线程对象,调用start()方法启动线程。

        public class TestThread01 extends Thread {
        
            @Override
            public void run() {
                //线程体
                for (int i = 0; i < 200; i++) {
                    System.out.println("子线程--" + i);
                }
            }
        
            public static void main(String[] args) {
                TestThread01 thread01 = new TestThread01();
                thread01.start();
                for (int i = 0; i < 1000; i++) {
                    System.out.println("主线程--" + i);
                }
            }
        }
        
      • 线程开启不一定立即执行,由CPU调度执行。主线程和子线程是交替执行的。

    • 实现Runnable接口:

      • 实现run()方法,编写线程执行体。创建线程对象(传入实现类),调用start()方法启动线程。

        public class TestThread02 implements Runnable {
        
            @Override
            public void run() {
                for (int i = 0; i < 200; i++) {
                    System.out.println("子线程--" + i);
                }
            }
        
            public static void main(String[] args) {
                TestThread02 thread02 = new TestThread02();
                //传入实现类
                new Thread(thread02).start();
                for (int i = 0; i < 1000; i++) {
                    System.out.println("主线程--" + i);
                }
            }
        }
        
      • Java是单继承多实现,推荐使用实现Runnable接口的方法,方便一个对象被多个线程调用。

    • 实现Callable接口:

  • 初识并发问题:

    • 多个线程操作同一个资源的情况下,线程不安全,数据紊乱。

      public class TestThread03 implements Runnable {
      
          private int ticketNum = 10;
          @Override
          public void run() {
              while (true) {
                  if(ticketNum<=0) {
                      break;
                  }
                  //延时
                  try {
                      Thread.sleep(200);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
       			//获取当前运行的线程名称          
                  System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票");
              }
          }
      
          public static void main(String[] args) {
              TestThread03 testThread03 = new TestThread03();
      		//同一个对象传入多个线程,设置线程名字
              new Thread(testThread03,"线程A").start();
              new Thread(testThread03,"线程B").start();
              new Thread(testThread03,"线程C").start();
          }
      }
      

2、先备知识

  • 静态代理:

    • 代理对象和真实对象都要实现同一个接口。代理对象要代理真实对象。

    • 代理对象做真实对象做不了的事情,真实对象专注做自己的事情。

    • Thread对象和传入的线程对象都实现了Runnable接口,Thread对象时传入的线程对象的静态代理。

      public class TestThread04 {
          public static void main(String[] args) {
              //类比 new Thread(testThread).start();
              new WeddingCompany(new You()).HappyMarry();
          }
      }
      
      
      interface Marry {
          public void HappyMarry();
      }
      
      class You implements Marry{
      
          @Override
          public void HappyMarry() {
              System.out.println("Marry");
          }
      }
      
      class WeddingCompany implements Marry{
          private Marry target;
      
          public WeddingCompany(Marry target) {
              this.target=target;
          }
      
          @Override
          public void HappyMarry() {
              before();
              this.target.HappyMarry();
              after();
          }
      
          private void after() {
              System.out.println("after");
          }
      
          private void before() {
              System.out.println("before");
          }
      }
      
  • Lamda表达式:

    • 避免匿名内部类定义过多,属于函数式编程的概念。

    • 函数式接口:只包含一个抽象方法的接口。

    • Lamda表达式的简化过程:

      • 外部实现类 -> 静态内部类 -> 局部内部类 -> 匿名内部类 -> Lamda表达式。
      public class LamdaFunction {
          //3、静态内部类
          static class ILike2 implements Like {
              @Override
              public void lamda(int p) {
                  System.out.println("静态内部类"+ p);
              }
          }
          public static void main(String[] args) {
              Like like1 = new ILike1();
              like1.lamda(1);
              Like like2 = new ILike2();
              like2.lamda(2);
              //4、局部内部类
              class ILike3 implements Like {
                  @Override
                  public void lamda(int p) {
                      System.out.println("局部内部类"+ p);
                  }
              }
              Like like3 = new ILike3();
              like2.lamda(3);
              //5、匿名内部类
              Like like4 = new Like() {
                  @Override
                  public void lamda(int p) {
                      System.out.println("匿名内部类"+ p);
                  }
              };
              like4.lamda(4);
              //6、Lamda表达式
              Like like5 = (int p) -> {
                  System.out.println("Lamda表达式"+ p);
              };
              like5.lamda(5);
              //7、简化的Lamda表达式
              Like like6 = p -> System.out.println("简化的Lamda表达式"+ p);
              like6.lamda(6);
          }
      }
      //1、函数式接口
      interface Like {
          public void lamda(int p);
      }
      //2、外部实现类
      class ILike1 implements Like {
          @Override
          public void lamda(int p) {
              System.out.println("外部实现类"+ p);
          }
      }
      

3、线程状态

  • 线程状态:

  • 线程停止:

    • 不推荐使用JDK提供的已经过时的线程停止方法。
    • 建议线程正常停止。可以使用标志位,对外提供方法改变表示,使线程停止下来。
    public class TestStop implements Runnable {
        private boolean flag = true;
        @Override
        public void run() {
            int i = 0;
            while (flag){
                System.out.println("thread run..." + i++);
            }
        }
    
        public void stop(){
            this.flag=false;
        }
    
        public static void main(String[] args) {
            TestStop testStop = new TestStop();
            new Thread(testStop).start();
            for (int i = 0; i < 100; i++) {
                System.out.println("main线程" + i);
                if(i==90){
                    testStop.stop();
                    System.out.println("线程停止");
                }
            }
        }
    }
    
  • 线程休眠:

    • Thread.sleep()指定当前线程阻塞的毫秒数。存在异常InterruptedException。
    • 时间达到后线程进入就绪状态。
    • 每一个对象都有一个锁,sleep不会释放锁。
  • 线程礼让:

    • Thread.yield()礼让线程,让正在执行的线程暂停,但不阻塞。
    • 将线程从运行状态转为就绪状态。
    • CPU重新调度,礼让不一定成功。
  • 线程强制执行:

    • thread.join()合并线程,待此线程执行完成之后,再执行其他线程,其他线程阻塞。可以理解为插队。

      public class TestJoin implements Runnable {
          @Override
          public void run() {
              for (int i = 0; i < 100; i++) {
                  System.out.println("子线程join" + i);
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              TestJoin testJoin = new TestJoin();
              Thread thread = new Thread(testJoin);
              thread.start();
              for (int i = 0; i < 200; i++) {
                  if(i==100) {
                      thread.join();
                  }
                  System.out.println("main线程" + i);
              }
          }
      }
      
    • 线程在main线程执行到100之前都是CPU调度的,执行到100之后先执行子线程,main线程阻塞。

  • 观测线程状态:

    • Thread.State常量定义线程状态:

      • NEW:新生,尚未启动的线程。
      • RUNNABLE:Java虚拟机中执行的线程。
      • BLOCKED:被阻塞等待监视器锁定的线程。
      • WAITING:正在等待另一个线程执行特定动作的线程。
      • TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程。
      • TERMINATED:已经退出的线程。
      public class TestState implements Runnable {
          @Override
          public void run() {
              for (int i = 0; i < 5; i++) {
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              TestState testState = new TestState();
              Thread thread = new Thread(testState);
      
              Thread.State state = thread.getState();
              System.out.println(state);
      
              thread.start();
              state = thread.getState();
              System.out.println(state);
      
              while (thread.getState()!=Thread.State.TERMINATED) {
                  Thread.sleep(100);
                  state = thread.getState();
                  System.out.println(state);
              }
              state = thread.getState();
              System.out.println(state);
          }
      }
      
  • 线程优先级:

    • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器安装优先级判断应该调度那个线程来执行。
    • 线程优先级高只是意味着获得调度的概率低,更可能优先执行,不是一定优先执行。
    • 线程的优先级用数字表示,范围从1到10。常量包括:
      • Thread.MIN_PRIORITY=1
      • Thread.MAX_PRIORITY=10
      • Thread.NORM_PRIORITY=5.
    • 使用方法getPriority()来获取优先级。
    • 使用方法setPriority(int xxx)来设置优先级。
  • 守护线程:

    • 线程分为用户线程和守护(daemon)线程。
    • 虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕。
    • 设置线程为守护线程thread.setDaemon(true)

iwehdio的博客园:https://www.cnblogs.com/iwehdio/
来源与结束于否定之否定。
原文地址:https://www.cnblogs.com/iwehdio/p/13561593.html