Java 多线程基础

347 阅读5分钟

1、进程、线程

进程:操作系统分配资源的最小单位

线程:操作系统调动的最小单位

2、使用多线程

  1. 继承 Thread

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
        }
    }
    
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            System.out.println("thread running");
        }
    
    }
    

    输出:

    thread running

    缺点:不支持多继承

  2. 实现 Runnable 接口

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread myThread = new MyThread();
            Thread thread = new Thread(myThread);
            thread.start();
        }
    }
    
    class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("thread running");
        }
    
    }
    

    输出:

    thread running

    优点:“间接”支持多继承

  3. 分析多线程的命令

    • jps+jstack.exe
    • jmc.exe(推荐)
    • jvisualvm.exe

3、currentThread() 方法

​ 作用:当前代码块正在被哪个线程调用

​ 例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(currentThread().getName());
    }
}

​ 输出:

Thread-0

4、isAlive() 方法

​ 作用:当前线程是否处于活动状态(正在运行、准备开始运行)

​ 例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        System.out.println("start->" + thread.isAlive());
        thread.start();
        Thread.sleep(100);
        System.out.println("end->" + thread.isAlive());
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("run->" + isAlive());
    }
}

​ 输出:

start->false
run->true
end->false

5、sleep(long millis) 方法和sleep(long millis, int nanos) 方法

​ 作用:在指定的时间内(毫秒/毫秒+纳秒)内让“正在执行的线程”休眠,正在执行的线程指 this.currentThread() 返回的线程

​ 例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("start time->"+System.currentTimeMillis());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end time->  "+System.currentTimeMillis());
    }
}

​ 输出:

start time->1637762721620
end time-> 1637762723623

6、getId() 方法

​ 作用:获得线程的唯一标识

​ 例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getId());
    }
}

​ 输出:

1

7、停止线程

  1. 判断线程是否为停止状态
    • public boolean isInterrupted()

      作用:测试 currentThread() 是否已经中断,不清除状态标志

      例子:

      public class Run {
          public static void main(String[] args) throws InterruptedException {
              MyThread thread = new MyThread();
              thread.start();
              Thread.sleep(100);
              thread.interrupt();
              System.out.println("is thread stop:"+thread.isInterrupted());
              System.out.println("is thread stop:"+thread.isInterrupted());
              System.out.println("end");
          }
      }
      
      class MyThread extends Thread {
          @Override
          public void run() {
              super.run();
              for (int i = 0; i < 1000; i++) {
                  System.out.println("i=" + i);
              }
          }
      }
      

      输出:

      i=755
      i=756
      is thread stop:true
      is thread stop:true
      i=757
      i=758

    • public static boolean interrupted()

      作用:测试 this 关键字所在的类是否已经中断,执行后具有清除状态标志为 false 的功能

      例子:

      public class Run {
          public static void main(String[] args) throws InterruptedException {
              Thread.currentThread().interrupt();
              System.out.println("is thread stop:" + Thread.interrupted());
              System.out.println("is thread stop:" + Thread.interrupted());
              System.out.println("end");
          }
      }
      

      输出:

      is thread stop:true
      is thread stop:false
      end

      第二次输出 false 的原因:

      interrupted() 具有清除状态的功能,第一次调用时,打印了当前线程被中断,然后将状态清除,此时当前线程的状态为不中断;在第二次调用时,由于上次的调用原因,此时的线程状态已经是不中断,所以打印为 false

  2. 异常法停止线程

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(100);
            thread.interrupt();
        }
    }
    
    class MyThread extends Thread {
    
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0; i < 2000; i++) {
                    if (Thread.interrupted()) {
                        System.out.println("already stop,exit");
                        throw new InterruptedException();
                    }
                    System.out.println("i=" + i);
                }
                System.out.println("end");
            } catch (InterruptedException e) {
                System.out.println("enter exception");
            }
    
        }
    }
    

    输出:

    i=686
    i=687
    already stop,exit
    enter exception

  3. stop() 法暴力停止线程

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(50);
            thread.stop();
        }
    }
    
    class MyThread extends Thread {
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 1000; i++) {
                System.out.println("i=" + i);
            }
        }
    }
    

    输出:

    i=374
    i=375
    i=376
    i=377
    i=378

    缺点:

    stop() 会释放锁导致数据不一致,已被弃用

  4. return 法停止线程

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(50);
            thread.interrupt();
        }
    }
    
    class MyThread extends Thread {
    
        @Override
        public void run() {
            while (true) {
                if (this.isInterrupted()) {
                    System.out.println("stop");
                    return;
                }
                System.out.println(System.currentTimeMillis());
            }
        }
    }
    

    输出:

    1637766071932
    1637766071932
    1637766071932
    stop

  5. sleep 状态下停止线程

    不管调用顺序,只要 interrupt()stop() 碰到一起就会出现异常

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(200);
            thread.interrupt();
        }
    }
    
    class MyThread extends Thread {
    
        @Override
        public void run() {
            try {
                System.out.println("thread begin");
                Thread.sleep(20000);
                System.out.println("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
    

    输出:

    thread begin
    java.lang.InterruptedException: sleep interrupted
         at java.base/java.lang.Thread.sleep(Native Method)
         at demo.MyThread.run(Run.java:18)

8、暂停线程

  1. suspend()resume() 的使用

    例子:

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(5000);
            thread.suspend();
            System.out.println("A=" + System.currentTimeMillis() + " i=" + thread.getI());
            Thread.sleep(5000);
            System.out.println("A=" + System.currentTimeMillis() + " i=" + thread.getI());
    
            thread.resume();
            Thread.sleep(5000);
    
            thread.suspend();
            System.out.println("B=" + System.currentTimeMillis() + " i=" + thread.getI());
            Thread.sleep(5000);
            System.out.println("B=" + System.currentTimeMillis() + " i=" + thread.getI());
    
        }
    }
    
    class MyThread extends Thread {
    
        private long i = 0;
    
        public long getI() {
            return i;
        }
    
        public void setI(long i) {
            this.i = i;
        }
    
        @Override
        public void run() {
            while (true) {
                i++;
            }
        }
    }
    

    输出:

    A=1637855269659 i=8483654775
    A=1637855274661 i=8483654775
    B=1637855279666 i=17266749694
    B=1637855284669 i=17266749694

  2. suspend()resume() 的缺点

    • 独占
    • 数据不完成
  3. suspend()resume() 的替代

    suspend() -> wait()

    resume() -> notify() / notifyAll()

9、yield() 方法

​ 作用:放弃当前的 CPU 资源,让其他任务去占用 CPU 时间片

​ 例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
        MyThreadYield threadYield=new MyThreadYield();
        threadYield.start();
    }
}

class MyThread extends Thread {

    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < 200000; i++) {

        }
        long endTime = System.currentTimeMillis();
        System.out.println("time:" + (endTime - beginTime));
    }
}

class MyThreadYield extends Thread {

    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < 200000; i++) {
            Thread.yield();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("time:" + (endTime - beginTime));
    }
}

​ 输出:

time:1
time:24

10、线程的优先级

设置优先级 public final void setPriority(int newPriority)

预定三个优先级:

    /**
     * The minimum priority that a thread can have.
     */
    public static final int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public static final int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public static final int MAX_PRIORITY = 10;

优先级具有继承性,例如A线程启动B线程,则B的优先级和A一样

11、守护线程

Java中存在两种线程:用户线程(非守护线程)、守护线程

只有进程中不存在非守护线程,则守护线程自动销毁;若存在任何一个非守护线程,守护线程就要继续工作

主线程(main)属于非守护线程

设置守护线程:setDaemon(true),需要在 start() 方法前设置

例子:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(5000);
        System.out.println("exit");
    }
}

class MyThread extends Thread {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            System.out.println("i=" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

i=1
i=2
i=3
i=4
i=5
exit