1、继承Thread类接口
public class ThreadCreate1 extends Thread {
@Override
public void run() {
System.out.println("这里写线程的任务逻辑");
}
public static void main(String[] args) {
// 创建线程
ThreadA thread = new ThreadA();
// 启动线程
thread.start();
}
}
2、实现Runnable接口
public class ThreadCreate2 {
public static void main(String[] args) {
// 创建Runnable任务对象
Runnable runTask = new Runnable() {
@Override
public void run() {
System.out.println("这里写线程的任务逻辑");
}
};
// 创建线程
Thread thread = new Thread(runTask);
// 启动线程
thread.start();
}
}
3、实现Callable接口
public class ThreadCreate3 {
public static void main(String[] args) {
// 创建Runnable任务对象
Callable callTask = new Callable<String>() {
@Override
public String call() {
System.out.println("这里写线程的任务逻辑");
return "ok";
}
};
// 创建FutureTask
FutureTask futureTask = new FutureTask(callTask);
// 创建线程
Thread thread = new Thread(futureTask);
// 启动线程
thread.start();
}
}
4、创建创建线程池
public class ThreadCreate4 {
public static void main(String[] args) {
// 创建固定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 传入Runnable接口匿名内部类对象
executorService.submit(() -> {
System.out.println("线程执行任务逻辑处理...");
});
// 传入Callable接口匿名内部类对象
executorService.submit(()->{
System.out.println("这里写线程的任务逻辑");
return "ok";
});
}
}
线程的几个常见问题。
1、开启线程是否可以通过线程对象调用run方法开启?
答:不可以,只能通过调用start方法,jvm才会开启线程。否则只能认为是普通对象对普通成员函数的一次调用。
2、问:继承Thread方式创建线程和实现Runable接口方式创建线程哪个好?
答:实现Runable接口方式较好。
因为:1是java中类是单一继承原则,一旦该类采用继承Thread方式创建线程,该类就没办法再继承别的类了,这样就不利于扩展了。
2、另外实现的方式更加符合面向对象思维,实现Runable接口就像是任务与执行分离。任务是任务,执行者是执行者。任务封装好可以交给不同的执行者执行。
3、问:实现Runable和实现Callable的区别。
答:实现Runable是重写run方法,run方法是void 无返回值。线程在执行完没有返回数据出来,无法得知线程执行是否完成。而实现Callable接口是重写call函数,该函数有返回值,线程执行完会返回数据出来,可以监控线程执行结果状态。另外Callable常常结合FutureTask联合使用。
4、问:为什么使用线程池?
答:因为创建线程相比创建普通对象要消耗较多的内存和时间,销毁线程释放线程对象也要消耗较多的时间。鉴于创建和销毁对资源(空间和时间)有较大的影响,所以内存中时刻维护一定数量的线程,当用到的时候直接使用时提供响应,减少资源浪费的好处。所以事先创建一定数量的线程维护在内存中就是线程池的初衷。数据库连接池也是一样,都是为了减少频繁创建对象对资源的浪费。