一、方法一(继承Thread类)
定义类继承Thread,重写run方法,把新线程要做的事写在run方法中,创建线程对象,开启新线程, 内部会自动执行run方法。
public class Demo2 {
public static void main(String[] args) {
MyThread mt = new MyThread(); //4,创建Thread类的子类对象
mt.start(); //5,开启线程
for(int i = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}
class MyThread extends Thread { //1,继承Thread
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaa");
}
}
}
public static void main(String[] args) {
new Thread() { //1,继承Thread类
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaaaa");
}
}
}.start(); //4,开启线程
}
二、方法二(实现Runnable接口)
定义类实现Runnable接口,实现run方法,把新线程要做的事写在run方法中,创建自定义的Runnable的子类对象,创建Thread对象, 传入Runnable,调用start()开启新线程,内部会自动调用Runnable的run()方法。
Thread(Runnable target)
分配新的Thread
对象。Thread(Runnable target, String name)
分配新的Thread
对象。
public class Demo3_Thread {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable(); //4,创建Runnable的子类对象
//Runnable target = mr; mr = 0x0011
Thread t = new Thread(mr); //5,将其当作参数传递给Thread的构造函数
t.start(); //6,开启线程
for(int i = 0; i < 1000; i++) {
System.out.println("bb");
}
}
}
class MyRunnable implements Runnable { //1,定义一个类实现Runnable
@Override
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaa");
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() { //1,将Runnable的子类对象传递给Thread的构造方法
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("bb");
}
}
}).start(); //4,开启线程
}
三、两种方式的区别
- 继承Thread
由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法
好处:可以直接使用Thread类中的方法,代码简单
弊端:如果已经有了父类,就不能用这种方法
- 实现Runnable接口
构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
好处:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
四、方法三(实现Callable接口)
Callable 接口类似于 Runnable
,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
public class Demo3_Thread {
public static void main(String[] args) throws Exception{
MyCallable mc = new MyCallable(100);
FutureTask<Integer> f = new FutureTask<>(mc);
new Thread(f).start();
System.out.println(f.get());
}
}
class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}
五、方法四(线程池)
1.概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。
2.Executors工厂类
static ExecutorService
newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
static ExecutorService
newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法:
<T> Future<T>
submit(Callable<T> task)
提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。Future<?>
submit(Runnable task)
提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。
V
get()
如有必要,等待计算完成,然后获取其结果。
3.使用步骤
- 创建线程池对象
- 创建Runnable实例
- 提交Runnable实例
- 关闭线程池
4.代码
①提交Runnable
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//结束线程池
pool.shutdown();
②提交Callable
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 结束
pool.shutdown();
public class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}
好处:
- 可以有返回值
- 可以抛出异常
弊端:
- 代码比较复杂,所以一般不用