线程(Thread)概述

线程是一个程序内部的一条执行流程。

多线程指的是从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行)。

多线程的创建方式

1.继承Thread。重写run方法,在run中执行

每次执行结果都会不一样

public class ThreadTest1 {
    // main方法是一条或多条线程负责执行。
    public static void main(String[] args) {
        // 创建MyThread类的对象代表一条线程
        Thread t = new MyThread();
        // 启动线程(会调用run方法)
        t.start();
        
        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出: " + i);
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出: " + i);
        }
    }
}

优点:编码简单

缺点:线程类已经继承Thread,无法继承其他类,不利于功能扩展。

注意事项:1.启动线程一定是start方法,不是run方法,否则就相当于单线程,执行完run才去执行后边程序。

   2. 不要把主线程任务放到子线程之前。这条和上一条恰恰相反。

2.实现Runnable接口

接口

public class MyRunnable implements Runnable {
    // 重写Runnable的run方法
    @Override
    public void run() {
        // 线程要执行的任务
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出 ===> " + i);
        }
    }
}

主函数

public class ThreadTest2 {
    public static void main(String[] args) {
        // 创建任务对象
        Runnable target = new MyRunnable();
        // 把任务对象交给一个线程对象处理
        new Thread(target).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出 ===> " + i);
        }
    }
}

优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。

3.方法二的匿名内部类写法

public class ThreadTest {
    public static void main(String[] args) {
        Runnable target = new MyRunnable();
        new Thread(target).start();

        // 简化形式1:使用匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程2输出: " + i);
                }
            }
        }).start();

        // 简化形式2:使用Lambda表达式
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("子线程3输出: " + i);
            }
        }).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出: " + i);
        }
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程1输出: " + i);
        }
    }
}

方法三:使用Callable接口

前两个线程创建方式都存在一个问题,假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。

多线程_主线程

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class test {
    public static void main(String[] args) throws Exception {
        // 3. 创建一个Callable的对象
        Callable<String> call = new MyCallable( 100);
        // 4. 把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务的作用?
        // 1. 是一个任务对象,实现了Runnable对象。
        // 2. 可以在线程执行完毕之后,用来任务对象调用get方法获取线程执行完毕后的结果值。
        FutureTask<String> f1 = new FutureTask<>(call);
        // 5. 把任务对象交给一个Thread对象
        new Thread(f1).start();

        // 6. 获取线程执行完毕后返回的结果。,结果可能正常,可能异常,需要把异常抛出去
//        执行到这里的时候,如果run方法没跑完,代码会在这里暂停,直到run方法执行完毕。
        String rs = f1.get();
        System.out.println(rs);
    }
}

接口

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        //线程任务,返回结果
//        需求:求1~n的和,并返回
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "1~" + n + "的和为:" + sum;
    }
}

优点:线程任务类只是实现接口,可以继续继承和实现接口,扩展性强。可以在线程执行完后获取线程执行的结果

缺点:编码复杂一点。

三种方法按需使用。

Thread常用方法

多线程_主线程_02

public class ThreadTest1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread("1号线程");
        // t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName()); // Thread-0

        Thread t2 = new MyThread("2号线程");
        // t2.setName("2号线程");
        t2.start();
        System.out.println(t2.getName()); // Thread-1

        // 主线程对象的名字
        // 主线程的名字:它负责把我们的代码执行。
        Thread m = Thread.currentThread();
        m.setName("主线程");
        System.out.println(m.getName()); // main

        for (int i = 1; i <= 5; i++) {
            System.out.println(m.getName() + "线程输出:" + i);
        }
    }
}
public class MyThread extends Thread {
    public MyThread(String name) {
        super(name); // 为当前线程设置名字
    }

    @Override
    public void run() {
        // 哪个线程执行它,它就会得到哪个线程对象。
        Thread t = Thread.currentThread();
        for (int i = 1; i <= 3; i++) {
            System.out.println(t.getName() + "输出:" + i);
        }
    }
}
public class ThreadTest2 {
   public static void main(String[] args) throws Exception {
    for (int i = 1; i <= 5; i++) {
        System.out.println(i);
        // 休眠5s
        if (i == 3) {
            // 会让当前执行的线程暂停5秒,再继续执行
            // 项目经理让我加上这行代码,如果用户发钱了,我就注释掉!
            Thread.sleep(5000);
        }
    }

    // join方法作用:让当前调用这个方法的线程先执行完。
    Thread t1 = new MyThread("1号线程");
    t1.start();
    t1.join();

    Thread t2 = new MyThread("2号线程");
    t2.start();
    t2.join();

    Thread t3 = new MyThread("3号线程");
    t3.start();
    t3.join();
}

}