很多学习Java基础的小伙伴肯定听说过多线程。那么我们有几种方式来创建线程呢?在jdk1.5或者jdk5之前有两种方式,一种是继承Thread类,另一种是实现Runnable接口。在jdk1.5后又为我们提供两种方式,一种是实现Callable接口,另一种就是使用线程池。下面我们来简单的说一下四种方式是如何创建线程的。
一、继承Thread类。
继承Thread类需要我们重写run()方法,且将核心代码写到run方法中。这里我们在创建的线程中打印1到100以内的偶数为例。代码如下:
package .java2;
/**
* @author summer
* @date 2022-01-11 10:03
*/
//首先我们需要继承Thread类
class NumTest extends Thread {
// 重写Thread类的run方法
@Override
public void run() {
// 将核心代码写到run方法里面
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
NumTest numTest=new NumTest();
// ("线程名"); 这里是给线程起一个名字
// 启动线程并调用run方法
();
// ();这里是错误的,对于继承的方式来讲,如果我们对已经start过的线程再调用start就会出错
// 正确的方式是如下方式,也就是需要我们重新创建一个对象来调用
// NumTest numTest1=new NumTest();
// ();
}
}
二、实现Runnable接口,同样是打印1到100以内的偶数为例:
package .java2;
/**
* @author summer
* @date 2022-01-11 10:09
*/
//实现Runnable接口
class NumTest1 implements Runnable {
// 重写run方法,并将核心代码写到run方法中
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
(i);
}
}
}
}
public class RunnableTest {
public static void main(String[] args) {
// 创建对象
NumTest1 numTest = new NumTest1();
// 创建Thread并将上步的对象作为参数传入构造器
Thread thread = new Thread(numTest);
// ("线程名");
// 启动线程
();
// 如果想再次执行的话,这种实现的方式只需要我们按照如下方式
// 再创建一个Thread对象,将实现类的想传入构造器,并调用
// Thread对象的start即可
// Thread thread1=new Thread(numTest);
// ();
}
}
三、实现Callable接口
通过上面的两个例子我们可以看出,不管是继承Thread类还是实现Runnable接口,其核心就是重写run方法,但是run方法的返回值是void也就是说没有返回值。而且run方法是不能抛出异常的,如果我们的业务复杂一下的话,需要捕获异常的话是捕获不到的。那么实现Callable接口的方式可以在我们重新call方法的时候抛出异常,也可以有返回值。比如,我们需要打印1到100以内的偶数,并需要返回偶数的和为例。
package .java2;
import ;
import ;
import ;
/**
* @author summer
* @date 2022-01-11 10:18
*/
//实现Callable接口,Callable接口是有泛型的,这个
//泛型就是我们我们调用call方法所需要返回的值的类型
class NumTest2 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
// 遍历1到100以内的所有偶数,并将所有偶数的和返回
if (i % 2 == 0) {
(i);
sum += i;
}
}
return sum;
}
}
public class CallableTest {
public static void main(String[] args) {
// 创建NumTest2对象
NumTest2 numTest2 = new NumTest2();
// 创建FutureTask对象 并将上步的对象作为参数传入
FutureTask<Integer> futureTask = new FutureTask<>(numTest2);
// 创建Thread类,并将上步对象传入
Thread thread = new Thread(futureTask);
//启动线程
();
try {
// 获取线程执行后返回的返回值,这一步不是必须的,如果你不需要返回值,则这一步可以不写
Integer integer = ();
("总和为:" + integer);
} catch (InterruptedException e) {
();
} catch (ExecutionException e) {
();
}
}
}
四、使用线程池
使用线程池的好处有以下几点
1.提高效率,不用重复的创建和销毁线程
2.提高利用率
3.可以对线程池进行一些设置
package .java2;
import .*;
/**
* @author summer
* @date 2022-01-11 10:25
*/
//实现遍历1到100以内的偶数
class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
(i);
}
}
}
}
//实现遍历 1到100以内的奇数,并将奇数的和返回
class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
// 遍历1到100以内的所有偶数,并将所有偶数的和返回
if (i % 2 != 0) {
(i);
sum += i;
}
}
return sum;
}
}
public class ThreadPollTest {
public static void main(String[] args) {
// 创建固定线程数量的线程池
ExecutorService service = (5);
RunnableDemo runnableDemo = new RunnableDemo();
// 如果是实现Runnable接口的方式则调用execute方式来执行
(runnableDemo);
CallableDemo callableDemo = new CallableDemo();
// 如果是实现Callable接口的方式则调用submit方式来执行
Future<Integer> future = (callableDemo);
try {
Integer integer = ();
("和为:" + integer);
} catch (InterruptedException e) {
();
} catch (ExecutionException e) {
();
}
// 最后要记得关闭线程池哦
();
}
}