多线程_基础知识

247 阅读5分钟

一、线程简介

程序、进程和线程的区别:

  • 程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

  • 进程:执行程序的一次执行过程,是系统资源分配的单位,是一个动态的概念。

  • 线程:CPU调度和执行的单位,一个进程中可以包含多个线程。

注意:真正多线程表示有多个CPU,即多核,例如服务器;如果只有一个CPU那只能算模拟多线程,因为在一个时间点里只能执行一个进程,因为进程切换速度快,给人多线程同时执行的错觉。

注意:线程不一定立即执行,需要CPU安排调度。

二、线程创建

创建线程的三种方式:继承Thread、实现Runnable接口、实现Callable接口

继承Thread

  • 编程思路
// 1.自定义线程类继承Thread类
public class ThreadDemo extends Thread{}
// 2.重写run()方法,编写线程执行体
@Override
public void run() {...}
// 3.创建线程对象,调用start()方法启动线程
new ThreadDemo().start();

不建议使用:避免OOP单继承局限性。

实现Runnable接口

  • 编程思路
// 定义线程类并实现Runnable接口
public class ThreadDemo implements Runnable{}
// 实现run()方法,编写线程执行体
@Override
public void run() {...}
// 创建线程对象,调用start()方法启动线程
new Thread(new ThreadDemo()).start();

推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用

实现Callable接口

  • 编程思路
// 1.实现Callable接口,需要返回值类型
public class ThreadDemo implements Callable<String>{}
// 2.重写call方法,需要抛出异常
@Override
public String call() {...}
// 3.创建目标对象
ThreadDemo callThread = new ThreadDemo();
// 4.创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
// 5.提交执行
Future<String> result = ser.submit(callThread);
// 6.获取结果
String strResult = resule.get();
// 7.关闭服务
ser.shutdownNow();

使用区别:可以定义返回值,可以抛出异常。

三、线程状态

线程五大状态:创建、就绪、运行、阻塞、死亡

线程六大状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

  • NEW(创建):尚未启动的线程处于此状态。
  • RUNNABLE(运行):在Java虚拟机中执行的线程处于该状态。
  • BLOCKED(阻塞):等待监视器锁被阻止的线程处于此状态。
  • WAITING(等待):无限期等待另一个线程执行特定操作的线程处于此状态。
  • TIMED_WAITING(超时等待):在指定的等待时间内等待另一个线程执行操作的线程处于此状态。
  • TERMINATED(终止):已退出的线程处于此状态。
package com.demo.thread;

public class ThreadDemo06 {
    static class MyThread implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        Thread t = new Thread(thread);
        System.out.println(t.getState()); // NEW

        t.start();
        System.out.println(t.getState()); // RUNNABLE

        while (Thread.State.TERMINATED != t.getState()) {
            Thread.sleep(200);
            System.out.println(t.getState()); // TIMED_WAITING
        }
        // 最后一次 TERMINATED
    }
}

四、线程方法

线程停止(stop)

  • 不推荐使用JDK提供的stop()、destroy()方法。[@Deprecated]
  • 推荐使用标志位变量来终止线程运行。
package com.demo.thread;

public class ThreadDemo02 extends Thread{
    // 自定义线程状态变量
    private boolean status = true;

    @Override
    public void run() {
        int i = 0;
        while (status) {
            System.out.println("该线程正在执行" + (i++));
        }
    }
    
    // 修改线程状态变量
    public void stopThread() {
        status = false;
    }

    public static void main(String[] args) {
        try {
            ThreadDemo02 thread = new ThreadDemo02();
            thread.start();
            Thread.sleep(1000);
            thread.stopThread();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程休眠(sleep)

  • Thread.sleep(long millis); 表示当前线程阻塞的毫秒数。
  • 使用sleep()方法线程进入阻塞状态,时间到达后线程进入就绪状态。
  • 每个对象都有一个锁,sleep()不会释放锁。
package com.demo.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadDemo03 extends Thread{
    private boolean status = true;

    @Override
    public void run() {
        int i = 0;
        while (status) {
            try {
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(1000);
                if (i++ > 10) {
                    status = false;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadDemo03 thread = new ThreadDemo03();
        thread.start();
    }
}

线程礼让(yield)

  • yield(); 让当前线程暂停,但不阻塞。
  • 让当前线程从运行状态转为就绪状态,重新竞争CPU。
  • 让CPU重新调度,礼让不一定成功!
package com.demo.thread;

public class ThreadDemo04 {
    static class YieldThread implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ": 开始启动");
            // 线程礼让
            Thread.yield();
            System.out.println(Thread.currentThread().getName() + ": 结束关机");
        }
    }
    
    public static void main(String[] args) {
        YieldThread yield = new YieldThread();
        new Thread(yield, "线程A").start();
        new Thread(yield, "线程B").start();
        /* A礼让成功,B礼让失败 
         * 线程A: 开始启动   
         * 线程B: 开始启动   
         * 线程B: 结束关机   
         * 线程A: 结束关机   
         */
    }
}

线程强制执行(join)

  • join(); 待此线程完成后,在执行其他线程。
  • 可以理解位现实中的VIP插队。
package com.demo.thread;

public class ThreadDemo05 {
    static class JoinThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println("join线程正在运行" + (i++));
            }
        }
    }

    public static void main(String[] args) {
        JoinThread join = new JoinThread();
        Thread t = new Thread(join);
        t.start();

        for (int i = 0; i < 1000; i++) {
            if (i == 200) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main线程正在运行" + i);
        }
    }
}

五、其他

线程优先级(priority)

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

  • 线程的优先级用数字表示,范围从1~10

    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  • 设置线程优先级方法

    • getPriority();
    • setPriority(int newPriority);
  • 注意:

    • 调整线程的优先级只是调整了CPU的调度概率,并不是绝对的。
    • 优先级的设置,应该在start()方法执行之前。

守护线程(daemon)

  • Java线程分为用户线程和守护线程

    • 用户线程:JVM必须等用户线程全部结束才能结束。eg. main线程
    • 守护线程:JVM不用等守护线程结束,也可以结束。eg.GC线程
  • 设置守护线程的方法

    • Thread.setDaemon(true); // 表示将该线程设置位守护线程
    • Thread.setDaemon(false); // 用户线程,默认值位false;
  • 注意:

    • 设置守护现在要在线程启动之前,要不然会报错IllegalThreadStateException异常
    • daemon默认位false,所以线程默认位用户线程