1. 继承Jave.lang包中的Thread类,重写Thread类中的run()方法,在run方法中编写实现多线程代码
  2. 实现Jave.lang.Runnable接口,在run方法中编写实现多线程代码
  3. 实现Java.util.concurrent.Callable接口,重写Call()方法并通过使用Future接口获取call()方法返回的结果
  4. 利用线程池创建多线程

1.继承Thread类创建多线程

class MyThreads extends Thread{
    /**
     * 重写run方法,定义了你想让多线程干啥
     */
    @Override
    public void run(){
        while (true) {
            System.out.println("这里是子线程哈哈哈。。。。");
        }
    }
}
public class Hello {
    public static void main(String[] args)  {
        //创建一个新线程
        MyThread t1 =new MyThreads();
        //调用start方法启动子线程,执行的还是run方法
        t1.start();

        //下面是main函数主线程代码
        while (true){
            System.out.println("这里是主线程------");
        }


    }
}

优点:编码简单

缺点:线程类已继承Thread类,无法继承其他类,不利于拓展

注意:

  1. 如果不对run方法重写将会被当作普通方法执行,此时相当于只运行主线程
  2. 如果把主线程任务放在子线程前面则只有等主线程跑完才会进行子线程,这是相当于还是单线程
  3. 通过start方法开启线程

2.实现Runnable接口

步骤:

  1. 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
  2. 创建MyRunnable任务对象
  3. 把MyRunnable任务对象交给Thread处理
  4. 调用线程对象的start()方法启动线程

Thread的构造器:

public Thread(String name) 可以为当前线程指定名称

public Thread(Runnable target) 封装Runnable对象成为线程对象

public Thread(Runnable target ,String name ) 封装Runnable对象成为线程对象,并指定线程名称

优缺点:

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强

缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的

class MyRunnable implements Runnable{
    /**
     * 重写run方法,定义了你想让多线程干啥
     */
    @Override
    public void run(){
        while (true) {
            System.out.println("这里是子线程哈哈哈。。。。");
        }
    }
}
public class Hello {
    public static void main(String[] args)  {
        //创建一个任务对象
        MyRunnable target = new MyRunnable();
        //把任务对象交给Thread处理
        Thread t1 = new Thread(target,"狗蛋二号");
        Thread t2 = new Thread("狗蛋二号");
        Thread t3 = new Thread(target);
        //调用start方法启动子线程,执行的还是run方法
        t1.start();

        //下面是main函数主线程代码
        while (true){
            System.out.println("这里是主线程------");
        }


    }
}

3.实现Callable接口

前两种方式都存在一个问题:重写的run方法均不能直接返回结果,不适合需要返回线程执行结果的场景。而通过实现Callable接口则可以做到这一点。

执行步骤:

  1. 得到任务对象定义类实现Callable接口,重写call方法,封装要做的事情,用FutureTask把Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果

优缺点:

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

缺点:编码复杂一点

public class t9 {
    public static void main(String[] args) {
        // 3、创建Callable任务对象
        Callable<String> call = new MyCallable(100);
        // 4、把Callable任务对象 交给 FutureTask 对象
        //  FutureTask对象的作用1: 是Runnable的对象(实现了Runnable接口),可以交给Thread了
        //  FutureTask对象的作用2: 可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果
        FutureTask<String> f1 = new FutureTask<>(call);
        // 5、交给线程处理
        Thread t1 = new Thread(f1);
        // 6、启动线程
        t1.start();
        


        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> f2 = new FutureTask<>(call2);
        Thread t2 = new Thread(f2);
        t2.start();

        try {
            // 如果f1任务没有执行完毕,这里的代码会等待,直到线程1跑完才提取结果。
            String rs1 = f1.get();
            System.out.println("第一个结果:" + rs1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            // 如果f2任务没有执行完毕,这里的代码会等待,直到线程2跑完才提取结果。
            String rs2 = f2.get();
            System.out.println("第二个结果:" + rs2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 1、定义一个任务类 实现Callable接口  应该申明线程任务执行完毕后的结果的数据类型
 */
class MyCallable implements Callable<String>{
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    /**
     2、重写call方法(任务方法)
     */
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        return "子线程执行的结果是:" + sum;
    }
}

4.通过线程池创建线程

定义:

线程池就是事先将多个线程对象放到一个容器中,当使用时就不用new线程而是直接去池里拿线程即可,节省了开辟子线程的时间,提高代码的执行效率

工作原理:

  1. 线程池刚创建的时候,里面没有任何线程,等到有任务过来的时候才会创建线程
  2. 调用 execute()提交一个任务时,如果当前的工作线程数<corePoolsize,直接创建新线程执行任务
  3. 如果工作线程数>corePoolsize,就会将任务放进任务队列缓存
  4. 如果队列已满,但是当前线程数量<maximumPoolSize,则还是会创建线程执行任务
  5. 如果队列已满,但是当前线程数量>maximumPoolSize,则会执行拒绝策略
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐