并发编程-线程创建的方式

165 阅读3分钟

三种方式:

  • 直接定义一个类继承Thread类,重写run方法,创建线程对象调用线程对象的start()方法来启动。
  • 定义一个线程任务实现Runnable接口,重写run()方法,创建线程任务对象,把线程任务对象保证成线程对象,调用线程对象的start()方法启动线程
  • 定义一个线程任务实现callable接口

1.继承Thread的方式

实现步骤

  1. 定义一个线程类继承Thread类
  2. 重写run()方法
  3. 创建一个线程对象
  4. 调用线程对象的start()方法启动线程,最终还是执行run()方法

代码Demo

public class ThreadDemo {
    public static void main(String[] args) {
        // 3. 创建一个线程对象
        MyThread myThread = new MyThread();
        // 4. 调用线程对象的start()方法启动线程,最终还是执行run()方法
        myThread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main线程 == 》" + i);
        }

    }
}

// 1. 定义一个线程类继承Thread类
class MyThread extends Thread {

    // 2.重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程" + i);
        }
    }
}

2.实现Runnable接口

实现步骤

  1. 创建一个线程任务类实现Runnable接口
  2. 重写run()方法
  3. 创建一个线程任务对象(注意:线程任务对象不是线程对象,只是执行线程的任务的)
  4. 把线程任务对象包装成线程对象,且可以指定线程名称
  5. 调用线程对象的start()方法启动线程

Thread的构造器

public Thread(){}
public Thread(String name){}
public Thread(Runnable target){}
public Thread(Runnable target, String name){}

代码demo

public class ThreadDemo1 {
    public static void main(String[] args) {
        // 3. 创建一个线程任务对象(注意:线程任务对象不是线程对象,只是执行线程的任务的)
        MyRunnable target = new MyRunnable();
        // 4. 把线程任务对象包装成线程对象,且可以指定线程名称
        Thread thread = new Thread(target, "1号线程");
        // 5. 调用线程对象的start()方法启动线程
        thread.start();

        Thread t2 = new Thread(target);
        t2.start();
    }
}


// 1. 创建一个线程任务类实现Runnable接口
class MyRunnable implements Runnable{
    // 2. 重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " == > " + i);
        }
    }
}

3.实现callable接口

实现步骤

  1. 创建一个线程任务类实现callable接口,申明线程返回的结果类型
  2. 重写线程任务类的call方法
  1. 创建一个callable的线程任务对象
  2. 把callable任务对象包装成一个未来任务对象
  1. 把未来任务对象包装成线程对象
  2. 启动线程对象

代码Demo

public class ThreadDemo1 {
    public static void main(String[] args) {
        // 3. 创建一个callable的线程任务对象
        MyThread03 call = new MyThread03();
        // 4. 把callable任务对象包装成一个未来任务对象
        //        -- public FutureTask(Callable<V> callable) {}
        // 未来任务对象是啥,有啥用?
        //        -- 未来任务对象其实就是一个Runnable对象;这样就可以被包装成线程对象
        //        -- 未来任务对象可以在线程执行完毕之后去得到线程执行的结果
        FutureTask<String> task = new FutureTask<>(call);
        // 5. 把未来任务对象包装成线程对象
        Thread t = new Thread(task);
        // 6. 启动线程对象
        t.start();

        // 获取线程执行的结果,如果线程没有结果,让出cpu等线程执行完再来取结果
        try {
            String s = task.get();
            System.out.println(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 1. 创建一个线程任务类实现callable接口,申明线程返回的结果类型
class MyThread03 implements Callable<String> {

    // 2. 重写线程任务类的call方法
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 10 ; i++) {
            System.out.println(Thread.currentThread().getName() + " == >" + i);
            sum += i;
        }
        return Thread.currentThread().getName() + "执行的结果是:" + sum;
    }
}

总结

1. 多线程注意事项

  1. 线程启动必须调用start()方法,否则会被当成普通类处理
  2. 建议线程先注册子线程,主线程任务放在之后

2.三种方式的优缺点

1. 继承Thread方式

优点:编码简单 缺点:因为java只能单继承,类继承Thread类后无法再继承其他类,导致本类不能通过继承拓展功能

2. 实现Runnable接口方式

缺点

  • 代码复杂一点
  • 不能直接得到执行结果 优点:
  • 线程任务类只是实现Runnable接口,可以继承其他类
  • 同一个线程任务对象可以被包装成多个线程对象target
  • 适合多个相同的程序代码的线程去共享同一个资源
  • 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立
  • 线程池可以放入实现Runnable或callable线程任务对象

3.实现Callable接口方式

优点:

  • 线程任务类只是实现Callable接口,可以继承其他类
  • 同一个线程任务对象可以被包装成多个线程对象
  • 适合多个相同的程序代码的线程去共享同一个资源
  • 实现解耦操作,线程任务代码可以被多个线程共享,线程任务代码和线程独立
  • 线程池可以放入实现Runnable或callable线程任务对象

实现Callable接口和Runnable接口的区别

  1. 实现callable接口重写call()方法,实现Runnable接口重写run()方法
  2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值的
  3. call方法可以抛出异常,run方法不可以
  4. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果future.get()