(1)继承Thread类,重写run方法
(2)实现Runnable接口,重写run方法,将Runnable接口的实现类的实例对象作为Thread构造函数参数
(3)通过Callable和FutureTask创建线程
(4)通过线程池创建线程。线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度

(1)继承Thread类,重写run方法

public class TestThread extends Thread{
    public TestThread(){
        //编写子类的构造方法,可缺省
    }
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName());
    }
    public static void main(String[] args){
        TestThread thread1 = new TestThread();
        thread1.start();
        System.out.println(Thread.currentThread().toString());
    }
}

运行结果:

Thread[main,5,main]
Thread-0

(2)实现Runnable接口,重写run方法,将Runnable接口的实现类的实例对象作为Thread构造函数参数

public class TestThread2 {
    public static void main(String[] args){
        System.out.println(Thread.currentThread().getName());
        Thread t1 = new Thread(new MyThread());
        t1.start();
    }

}


class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

运行结果:

main
Thread-0

(3)通过Callable和FutureTask创建线程

  1. 创建Callable接口的实现类 ,并实现Call方法
  2. 创建Callable实现类的实例,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
  3. 使用FutureTask对象作为Thread对象的target创建并启动线程
  4. 调用FutureTask对象的get()来获取子线程执行结束的返回值
public class TestThread3 {
    public static void main(String[] args) {

        Callable<Object> oneCallable = new Tickets<Object>();
        FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);

        Thread t = new Thread(oneTask);

        System.out.println(Thread.currentThread().getName());

        t.start();

    }

}


class Tickets<Object> implements Callable<Object> {

    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
        return null;
    }
}

运行结果:

main
Thread-0-->我是通过实现Callable接口通过FutureTask包装器来实现的线程

(4)通过线程池创建线程

public class TestThread4 {

    //线程池数量
    private static int POOL_NUM = 10;


    public static void main(String[] args) throws InterruptedException {

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                5,10,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(1),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        for(int i = 0; i<POOL_NUM; i++){
            RunnableThread thread = new RunnableThread();

            //Thread.sleep(1000);
            threadPool.execute(thread);
        }
        //关闭线程池
        threadPool.shutdown();

    }

}

class RunnableThread implements Runnable {
    @Override
    public void run() {
        System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");
    }
}

运行结果:

通过线程池方式创建的线程:pool-1-thread-1 
通过线程池方式创建的线程:pool-1-thread-2 
通过线程池方式创建的线程:pool-1-thread-1 
通过线程池方式创建的线程:pool-1-thread-3 
通过线程池方式创建的线程:pool-1-thread-4 
通过线程池方式创建的线程:pool-1-thread-5 
通过线程池方式创建的线程:pool-1-thread-6 
通过线程池方式创建的线程:pool-1-thread-7 
通过线程池方式创建的线程:pool-1-thread-8 
通过线程池方式创建的线程:pool-1-thread-9

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

ThreadPoolExecutor 构造器如下

public ThreadPoolExecutor(
            int corePoolSize, //线程池核心池的大小。
            int maximumPoolSize, // 线程池的最大线程数。
            long keepAliveTime, // 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
            TimeUnit unit, // keepAliveTime 的时间单位。
            BlockingQueue<Runnable> workQueue, // 用来储存等待执行任务的队列。
            ThreadFactory threadFactory, // 线程工厂
            RejectedExecutionHandler handler)  // 拒绝策略
    )

创建方法的比较

基于线程池的创建方法只是在前几种创建方式的基础上增加了线程池,而实现Runnable和实现Callable接口的方式基本相同,不过是后者执行call()方法有返回值,因此,我们可以把这两种方法归为一种方式。因此,我们只要比较实现接口与继承Thread类:

(1)继承Thread的优缺点

优点:

  • 实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程;

缺点:

  • 线程类已经继承Thread类了,就不能再继承其他类;
  • 多个线程不能共享同一份资源

(2)实现接口的优缺点

优点:

  • 线程类只是实现了接口,还可以继承其他类;
  • 多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况。

缺点:

  • 通过这种方式实现多线程,相较于第一类方式,编程较复杂;
  • 要访问当前线程,必须调用Thread.currentThread()方法。

注:一般采用实现接口的方法实现多线程