程序员

一、快速认识多线程

2020-10-01  本文已影响0人  _Colbert

1.1 线程的介绍

​ 对于计算机来说,一个任务就是一个进程,而一个进程最少包含一个线程。每启动一个JVM虚拟机,就会启动一个线程。例如:在使用360安全卫士的时候,可以同时进行病毒查杀和垃圾清理,这就开启了两个不同的线程。

1.2 创建一个线程:

  1. 创建一个线程,重写run()方法。
  2. 启动线程,调用start()方法。
public static void main(String[] args) throws InterruptedException {

        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i <100 ; i++) {
                    System.out.println("听歌");
                    try {
                        
                        // 加上sleep让两个线程交替更明显
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("看视频");
            Thread.sleep(100);
        }

    }

听歌
看视频
听歌
看视频
听歌
看视频
听歌
......

1.3 线程的生命周期

​ 线程的生命周期大概可以分为五个部分:

image.png

1.3.1 NEW(新建)

​ 用new关键字创建一个线程而不调用start方法启动,这时候为Thread对象的NEW状态。在没有调用start方法之前,线程是不存在的,所以一般不说是线程的NEW状态。

1.3.2 RUNNABLE(可运行)

​ 调用start方法后,线程才被创建,此时该线程并不一定会立即执行,该线程必须等到CPU的调度才会执行,而等待的这个状态就是RUNNABLE状态。

1.3.3 RUNNING(运行)

​ 当CPU选中该线程时(通过轮询或其他方式),那么此时它才能真正执行自己的代码,此时进入RUNNING状态。

RUNNING转换为其他状态:

1.3.4 BLOCKED(阻塞)

​ 1.3.3中的介绍了进入BLOCKED状态的情况。

BLOCKED进入其他状态:

1.3.5 TERMINATED/DEAD(死亡)

​ TERMINATED是线程的最终状态,在该状态中不会再切换到其他状态,该状态意味着线程的生命周期结束。

进入TERMINATED状态有以下几种情况:

1.4 模板设计模式

从start()方法可以学习模板设计模式:

print类似Thread的start方法,wrapPrint类似run方法。

public class TemplateDemo {

    /**
     * 父类搭建好流程模板
     * @param message
     */
    public final void print(String message){
        System.out.println("########");
        warpPrint(message);
        System.out.println("########");
    }

    /**
     * 空的方法,让不同的子类去实现不同的逻辑
     * @param message
     */
    protected void warpPrint (String message){


    }

    public static void main(String[] args) {

        new TemplateDemo(){
            @Override
            protected void warpPrint(String message) {
                System.out.println(message);
            }
        }.print("hello");

        new TemplateDemo(){
            @Override
            protected void warpPrint(String message) {
                System.out.println(message);
            }
        }.print("java");
    }


########
hello
########
########
java
########

1.5 Runnable接口以及策略模式

很多文章说创建线程有两种方式:一种是继承Thread,另一种是实现Runnable接口。这种说法是不严谨的,只有Thread才是代表线程,而线程里面的逻辑可以由这两种方法来实现。

public class TicketWindowRunnable implements Runnable {
    
    // 没用static修饰,无论用static,还是采用这种Runnable的方式,都有线程安全问题。
    private int index = 1;
   
    private final static int MAX = 50;
    @Override
    public void run() {
        while (index < MAX) {
            System.out.println(Thread.currentThread() + "的号码是" + (index++));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class TestTicketWindow {
    public static void main(String[] args) {
        final TicketWindowRunnable task = new TicketWindowRunnable();
        new Thread(task, "一号窗口").start();
        new Thread(task, "二号窗口").start();
        new Thread(task, "三号窗口").start();
        new Thread(task, "四号窗口").start();
    }
}
Thread[一号窗口,5,main]的号码是1
Thread[二号窗口,5,main]的号码是2
Thread[四号窗口,5,main]的号码是3
Thread[三号窗口,5,main]的号码是4
Thread[三号窗口,5,main]的号码是5
Thread[四号窗口,5,main]的号码是6
Thread[二号窗口,5,main]的号码是7
Thread[一号窗口,5,main]的号码是8

note Thread.sleep()更优雅的写法

  1. Thread.sleep(10)
  2. Thread.sleep(10*1000);
  3. Thread.sleep(10601000);
  1. TimeUnit.MILLISECONDS.sleep(10);
  2. TimeUnit.SECONDS.sleep(10);
  3. TimeUnit.MINUTES.sleep(10);
上一篇 下一篇

猜你喜欢

热点阅读