Java多线程四种常用创建方式 | 【奔跑吧!JAVA】
【摘要】 Java多线程四种常用创建方式
方式一:继承Thread类的方式(JDK1.5之前)
1、创建步骤:
(1)定义子类继承Thread类。
(2)子类中重写Thread类中的run方法。
(3)创建Thread子类对象,即创建了线程对象。
(4)调用线程对象start方法:启动线程,调用run方法。
2、MyThread.java代码
public class MyThread extends Thread {
public MyThread() {
super();
}
/**
* 重写Thread类的run()
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
3、ThreadTest.java代码
public class ThreadTest {
public static void main(String[] args) {
// 创建Thread类的子类的对象
MyThread t1 = new MyThread();
// 启动线程;并调用当前线程的run()方法。
t1.start();
// 创建Thread类的子类的对象
MyThread t2 = new MyThread();
// 启动线程;并调用当前线程的run()方法。
t2.start();
}
}
4、注意点
(1)如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
(2)run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
(3)想要启动多线程,必须调用start方法。
(4)一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
方式二:实现Runnable接口的方式(JDK1.5之前)
1、创建步骤:
(1)定义子类,实现Runnable接口。
(2)子类中重写Runnable接口中的run方法。
(3)通过Thread类含参构造器创建线程对象。
(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
(5)调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
2、MyThread.java代码
public class MyThread implements Runnable {
/**
* 实现类去实现Runnable中的抽象方法:run()
*/
@Override
public void run() {
// 遍历100以内的偶数
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
3、ThreadTest.java代码
public class ThreadTest {
public static void main(String[] args) {
// 创建实现类的对象
MyThread myThread = new MyThread();
// 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(myThread);
t1.setName("线程1");
// 通过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()-->调用了Runnable类型的target的run()
t1.start();
// 再启动一个线程
Thread t2 = new Thread(myThread);
t2.setName("线程2");
t2.start();
}
}
4、方式一和方式二比较
开发中:优先选择:实现Runnable接口的方式
原因:(1) 实现的方式没类的单继承性的局限性
(2) 实现的方式更适合来处理多个线程共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
目前两种方式,要想启动线程,都是调用的Thread类中的start()。
方式三:实现Callable接口的方式(JDK5.0新增)
1、创建步骤:
(1)创建一个实现Callable的实现类
(2)实现call方法,将此线程需要执行的操作声明在call()中
(3)创建Callable接口实现类的对象
(4)将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
(5)将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
(6)获取Callable中call方法的返回值
2、MyThread.java代码
public class MyThread implements Callable<Integer> {
/**
* 实现call方法,将此线程需要执行的操作声明在call()中
*
*/
@Override
public Integer call() throws Exception {
int sum = 0;
// 计算100以内所有偶数的和
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
// 返回计算结果
return sum;
}
}
3、ThreadTest.java代码
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建Callable接口实现类的对象
Callable<Integer> callable = new MyThread();
// 将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
Thread t1 = new Thread(futureTask);
t1.start();
// 获取Callable中call方法的返回值
// get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Integer sum = futureTask.get();
System.out.println("总和为:" + sum);
}
}
4、说明
为何说Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
(1) call()可以返回值的。
(2) call()可以抛出异常,被外面的操作捕获,获取异常的信息。
(3) Callable是支持泛型的。
方式四:使用线程池
1、创建步骤:
(1)提供指定线程数量的线程池
(2)执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
(3)关闭连接池
2、实现Runnable接口方式
2.1、MyThread01.java代码
public class MyThread01 implements Runnable {
/**
* 实现类去实现Runnable中的抽象方法:run()
*/
@Override
public void run() {
// 打印100以内的所有偶数
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
2.2、MyThread02.java代码
public class MyThread02 implements Runnable {
/**
* 实现类去实现Runnable中的抽象方法:run()
*/
@Override
public void run() {
// 打印100以内的所有奇数
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
2.3、ThreadTest.java代码
public class ThreadTest {
public static void main(String[] args) {
// 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new MyThread01());
service.execute(new MyThread02());
// 关闭连接池
service.shutdown();
}
}
3、实现Callable接口方式
3.1、MyThread01.java代码
public class MyThread01 implements Callable<Integer> {
/**
* 实现call方法,将此线程需要执行的操作声明在call()中
*/
@Override
public Integer call() throws Exception {
int sum = 0;
// 计算100以内所有偶数的和
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
// 返回计算结果
return sum;
}
}
3.2、MyThread02.java代码
public class MyThread02 implements Callable<Integer> {
/**
* 实现call方法,将此线程需要执行的操作声明在call()中
*/
@Override
public Integer call() throws Exception {
int sum = 0;
// 计算100以内所有奇数的和
for (int i = 1; i <= 100; i++) {
if (i % 2 != 0) {
sum += i;
}
}
// 返回计算结果
return sum;
}
}
3.3、ThreadTest.java代码
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 创建Callable接口实现类的对象
Callable<Integer> callable1 = new MyThread01();
Callable<Integer> callable2 = new MyThread02();
// 将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask<Integer> futureTask1 = new FutureTask<>(callable1);
FutureTask<Integer> futureTask2 = new FutureTask<>(callable2);
// 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.submit(futureTask1);
service.submit(futureTask2);
// 获取Callable中call方法的返回值
Integer sum1 = futureTask1.get();
System.out.println("偶数总和为:" + sum1);
Integer sum2 = futureTask2.get();
System.out.println("奇数总和为:" + sum2);
// 关闭连接池
service.shutdown();
}
}
4、说明
使用线程池的好处:
(1)提高响应速度(减少了创建新线程的时间)
(2)降低资源消耗(重复利用线程池中线程,不需要每次都创建)
(3)便于线程管理
【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/265241
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)