线程的四种创建方式
第二节 线程的四种创建方式文章目录
前言
线程的创建主要有四种方式,如下:
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
4)使用线程池创建线程
一、继承Thread类创建线程
创建线程的步骤:
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
//1) 定义子类继承Thread类。
public class ThreadTest1 extends Thread {
//2) 子类中重写Thread类中的run方法。
@Override
public void run() {
//线程执行的内容
for (int i = 0; i <100 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
public static void main(String[] args) {
//3) 创建Thread子类对象,即创建了线程对象。
ThreadA threadA = new ThreadA();
ThreadA threadA1 = new ThreadA();
//设置线程的名字
threadA.setName("张三");
threadA1.setName("李四");
//4) 调用线程对象start方法
//开始执行线程
threadA.start();
threadA1.start();
}
}
注意:
- 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
- run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
- 想要启动多线程,必须调用start方法。
二、实现Runnable接口创建线程
创建线程的步骤:
- 定义子类,实现Runnable接口。
- 子类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
//1) 定义子类,实现Runnable接口。
public class ThreadTest2 implements Runnable {
//2) 子类中重写Runnable接口中的run方法。
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
public static void main(String[] args) {
ThreadTest2 threadTest2 = new ThreadTest2();
//3) 通过Thread类含参构造器创建线程对象。
//4) 将Runnable接口的子类对象(threadTest2)作为实际参数传递给Thread类的构造器中。
Thread thread1 = new Thread(threadTest2);
Thread thread2 = new Thread(threadTest2);
//设置名字
thread1.setName("张三");
thread2.setName("李四");
//5) 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
//启动线程
thread1.start();
thread2.start();
}
}
1.继承方式和实现方式的联系与区别
区别
- 继承Thread:线程代码存放Thread子类run方法中。
- 实现Runnable:线程代码存在接口的子类的run方法中。实现Runnable方式的好处
- 避免了单继承的局限性
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
三、实现Callable接口。 — JDK 5.0新增
- Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
- ExecutorService:线程池类
- Future
<T>submit(Callable<T>task):获取线程池中的某一个线程对象,并执行线程中的call()方法 - Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
//1.创建一个实现Callable的实现类
public class ThreadTest3 implements Callable {
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
//该方法为打印100内每个偶数,同时返回的结果是100内所有偶数的总和
int sum = 0;
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
ThreadTest3 threadTest3 = new ThreadTest3();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(threadTest3);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
1.与使用Runnable相比, Callable功能更强大些
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
四、使用线程池方式。 — JDK 5.0新增
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
使用线程池的好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
public class ThreadTest4 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public static void main(String[] args) {
ThreadTest4 t1 = new ThreadTest4();
ThreadTest4 t2 = new ThreadTest4();
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor)service;
//设置线程池的属性
//核心池的大小
poolExecutor.setCorePoolSize(15);
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(t1);//适合适用于Runnable
service.execute(t2);//适合适用于Runnable
//service.submit(Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown();
}
}
版权声明:本文为my2889214412原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。