Java创建线程有多种方式,在Java的面试过程中也是经常被问到的知识点。

  1. 继承Thread类  
  2. 实现runnable接口 
  3. 实现callable接口 
  4. 基于线程池创建线程

下面对这几钟方式进行讲解。

继承Thread类  

使用Thread类创建线程的方法如下:

  1. 创建一个类如MyThreadTask并继承Thread类,重写run方法,这个方法就是我们的线程的任务逻辑。
  2. 创建刚才定义的MyThreadTask类的对象如:myThreadTask。
  3. 调用myThreadTask对象的start方法,启动线程。

代码如下:

 public class MyThreadTask extends Thread {
    private int count = 5;
    @Override
    public void run() {
        while(count-- >0)
        {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程名" + this.getName() + ",次数(" + count + ")");
        }
    }
    public static void main(String[] args) {

        MyThreadTask task1 = new MyThreadTask();
        MyThreadTask task2 = new MyThreadTask();
        task1.start();
        task2.start();

    }
}

测试一下

输出:

线程名Thread-0,次数(4)
线程名Thread-1,次数(4)
线程名Thread-0,次数(3)
线程名Thread-1,次数(3)
线程名Thread-0,次数(2)
线程名Thread-1,次数(2)
线程名Thread-0,次数(1)
线程名Thread-1,次数(1)
线程名Thread-0,次数(0)
线程名Thread-1,次数(0)

 这里创建了两个线程对象,task1和task2,且变量count是独立的。

使用继承Thread类创建的多线程优缺点如下:

  • 优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
  • 缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。

实现runnable接口 

实现runnable接口创建线程和继承Thead类方法类似:

  1. 创建一个类如MyThreadTask并实现Runnable接口,重写run方法,这个方法就是我们的线程的任务逻辑。
  2. 创建刚才定义的MyThreadTask类的对象如:myThreadTask。
  3. 通过myThreadTask创建Thread对象,如task。
  4. 调用task对象的start方法,启动线程。

代码如下:

 public class MyThreadTask implements Runnable {
    private int count = 10;
    @Override
    public void run() {
        while(count-- >0)
        {          
            System.out.println("线程名" + Thread.currentThread().getName() + ",次数(" + count + ")");
        }
    }
    public static void main(String[] args) {
		Runnable myTask = new MyThreadTask();
        
        Thread task1 = new Thread(myTask);
        Thread task2 = new Thread(myTask);
        task1.start();
        task2.start();
        
    }
}

测试一下

输出:

线程名Thread-0,次数(9)
线程名Thread-0,次数(7)
线程名Thread-0,次数(6)
线程名Thread-0,次数(5)
线程名Thread-0,次数(4)
线程名Thread-0,次数(3)
线程名Thread-1,次数(8)
线程名Thread-1,次数(1)
线程名Thread-1,次数(0)
线程名Thread-0,次数(2)

它与继承Thread类不同,是多个线程共享同一个对象,所以,因 变量count是共享的,输出的结果若有不同。

可以使用ThreadLocal在实现Runnable多线程接口的情况下,达到count变量独享用的。

使用实现runnable接口 创建的多线程优缺点如下:
  • 优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  • 缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

实现callable接口 

实现callable接口创建线程的方法如下:

  1. 定义一个线程任务类MyThreadTask 实现Callable接口,声明线程执行的结果类型Boolean。
  2. 重写线程任务类的call()方法,这个方法可以直接返回执行的结果。
  3. 创建一个Callable的线程任务对象。
  4. 把Callable的线程任务对象包装成一个未来任务对象。
  5. 把未来任务对象包装成线程对象。
  6. 调用线程start()方法,启动线程。
  7. 获取线程执行结果。
代码如下:
 import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyThreadTask implements Callable<Boolean> {
    private int count = 10;
    public Boolean call() throws Exception {
        while(count-- >0)
        {
            System.out.println("线程名" + Thread.currentThread().getName() + ",次数(" + count + ")");
        }
        return false;
    }
    public static void main(String[] args) {
        MyThreadTask task1 = new MyThreadTask();
        MyThreadTask task2 = new MyThreadTask();
        
        FutureTask<Boolean> futureTask1 = new FutureTask<Boolean>(task1);
        FutureTask<Boolean> futureTask2 = new FutureTask<Boolean>(task2);

        Thread thread1 = new Thread(futureTask1);
        Thread thread2 = new Thread(futureTask2);

        thread1.start();
        thread2.start();

        try {
            Boolean s1 = futureTask1.get();
            Boolean s2 = futureTask2.get();

            System.out.println(s1);
            System.out.println(s2);

        } catch (Exception e) {
            e.printStackTrace();
        }



    }
} 

测试一下

实现callable接口 创建多线程的优缺点如下:

缺点:编程复杂。

优点:1)线程类只是实现了callable接口,还可以继承其他的类。2)可以使用线程池。3)可以得到线程执行结果.4)和runnable接口 不同的是,可以创建多个线程且独立。

基于线程池创建线程

基于线程池创建线程创建线程的方法如下:

实现步骤

  1. 创建一个类MyThreadTask ,继承callable接口。
  2. 在该类中实现Callable接口的call方法,并返回值。
  3. 创建 ExecutorService 实例和线程实例。
  4. 调用 ExecutorService 的submit方法,把线程实例提交到线程池中启动,并使用Future接受线程实例call方法返回的值
  5. 关闭 ExecutorService。

代码如下:

 public class MyThreadTask implements Callable<Boolean> {
	private String threadName;
	private int count = 10;
	public MyThreadTask(String name){
		this.threadName=name;
	}
	public Boolean call() {
		while(count-- >0)
        {          
            System.out.println("线程名" + threadName + ",次数(" + count + ")");
        }
		return true;
	}
	
	public static void main(String args[]) {
		MyThreadTask task1=new MyThreadTask("task1");
		MyThreadTask task2=new MyThreadTask("task2");
		ExecutorService ser=Executors.newFixedThreadPool(2);
		Future<Boolean> result1=ser.submit(task1);
		Future<Boolean> result2=ser.submit(task2);
		Boolean r1=false;
		Boolean r2=false;
		try {
			r1=result1.get();
			r2=result2.get();
		}catch (Exception e) {
			
		}
		System.out.println(r1);
		System.out.println(r2);
		ser.shutdown();
				
	}
}

测试一下

线程池的优缺点:

  1. 缺点:1)占用一定的内存空间。 2)线程越多CPU的调度开销越大。 3)程序的复杂度会上升。
  2. 优点:1)适当的提高程序的执行效率(多个线程同时执行)。 2)适当的提高了资源利用率(CPU、内存等)。