Java创建线程有多种方式,在Java的面试过程中也是经常被问到的知识点。
- 继承Thread类
- 实现runnable接口
- 实现callable接口
- 基于线程池创建线程
下面对这几钟方式进行讲解。
继承Thread类
使用Thread类创建线程的方法如下:
- 创建一个类如MyThreadTask并继承Thread类,重写run方法,这个方法就是我们的线程的任务逻辑。
- 创建刚才定义的MyThreadTask类的对象如:myThreadTask。
- 调用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)
线程名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类方法类似:
- 创建一个类如MyThreadTask并实现Runnable接口,重写run方法,这个方法就是我们的线程的任务逻辑。
- 创建刚才定义的MyThreadTask类的对象如:myThreadTask。
- 通过myThreadTask创建Thread对象,如task。
- 调用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-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接口创建线程的方法如下:
- 定义一个线程任务类MyThreadTask 实现Callable接口,声明线程执行的结果类型Boolean。
- 重写线程任务类的call()方法,这个方法可以直接返回执行的结果。
- 创建一个Callable的线程任务对象。
- 把Callable的线程任务对象包装成一个未来任务对象。
- 把未来任务对象包装成线程对象。
- 调用线程start()方法,启动线程。
- 获取线程执行结果。
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接口 不同的是,可以创建多个线程且独立。
基于线程池创建线程
基于线程池创建线程创建线程的方法如下:
实现步骤
- 创建一个类MyThreadTask ,继承callable接口。
- 在该类中实现Callable接口的call方法,并返回值。
- 创建 ExecutorService 实例和线程实例。
- 调用 ExecutorService 的submit方法,把线程实例提交到线程池中启动,并使用Future接受线程实例call方法返回的值
- 关闭 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)占用一定的内存空间。 2)线程越多CPU的调度开销越大。 3)程序的复杂度会上升。
- 优点:1)适当的提高程序的执行效率(多个线程同时执行)。 2)适当的提高了资源利用率(CPU、内存等)。