很多学习 Java 基础的小伙伴肯定听说过 多线程 。那么我们有几种方式来创建 线程 呢?在jdk1.5或者jdk5之前有两种方式,一种是继承 Thread 类,另一种是实现Runnable接口。在jdk1.5后又为我们提供两种方式,一种是实现Callable接口,另一种就是使用 线程池 。下面我们来简单地说一下四种方式是如何创建线程的。
一、继承Thread类。
继承Thread类需要我们重写run()方法,且将核心代码写到run方法中。这里我们在创建的线程中打印1到100以内的偶数为例。代码如下:
package com.aoshen.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) {
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
NumTest numTest=new NumTest();
// numTest.setName("线程名"); 这里是给线程起一个名字
// 启动线程并调用run方法
numTest.start();
// numTest.start();这里是错误的,对于继承的方式来讲,如果我们对已经start过的线程再调用start就会出错
// 正确的方式是如下方式,也就是需要我们重新创建一个对象来调用
// NumTest numTest1=new NumTest();
// numTest1.start();
}
}
二、实现Runnable接口,同样是打印1到100以内的偶数为例:
package com.aoshen.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) {
System.out.println(i);
}
}
}
}
public class RunnableTest {
public static void main(String[] args) {
// 创建对象
NumTest1 numTest = new NumTest1();
// 创建Thread并将上步的对象作为参数传入 构造器
Thread thread = new Thread(numTest);
// thread.setName("线程名");
// 启动线程
thread.start();
// 如果想再次执行的话,这种实现的方式只需要我们按照如下方式
// 再创建一个Thread对象,将实现类的想传入构造器,并调用
// Thread对象的start即可
// Thread thread1=new Thread(numTest);
// thread1.start();
}
}
三、实现Callable接口
package com.aoshen. java 2;
import java.util.concurrent.Callable;
import java.util.concurrent.Execution Exception ;
import java.util.concurrent.FutureTask;
/**
* @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) {
System.out.println(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);
//启动线程
thread.start();
try {
// 获取线程执行后返回的返回值,这一步不是必须的,如果你不需要返回值,则这一步可以不写
Integer integer = futureTask.get();
System.out.println("总和为:" + integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
四、使用线程池
使用线程池的好处有以下几点
1.提高效率,不用重复的创建和销毁线程
2.提高利用率
3.可以对线程池进行一些设置
package com.aoshen.java2;
import java.util.concurrent.*;
/**
* @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) {
System.out.println(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) {
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadPollTest {
public static void main(String[] args) {
// 创建固定线程数量的线程池
Executor Service service = Executors.newFixedThreadPool(5);
RunnableDemo runnableDemo = new RunnableDemo();
// 如果是实现Runnable接口的方式则调用execute方式来执行
service.execute(runnableDemo);
CallableDemo callableDemo = new CallableDemo();
// 如果是实现Callable接口的方式则调用submit方式来执行
Future<Integer> future = service.submit(callableDemo);
try {
Integer integer = future.get();
System.out.println("和为:" + integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 最后要记得关闭线程池哦
service.shutdown();
}
}