提示:阅读本篇文章前,请先了解操作系统中线程和进程的基本概念。
线程基本概念本文会有所提及,操作系统和进程请参考我的这两篇笔记 https://note.youdao.com/s/ag3ipekEhttps://note.youdao.com/s/ag3ipekE
https://note.youdao.com/s/BFTOv61yhttps://note.youdao.com/s/BFTOv61y
前言:
在系统层面,为了减少操作的耗时,提高程序并行操作效率以及CPU的利用率,我们引入了多线程的技术概念。、
本文将从线程的基本概念讲起,详细介绍创建多线程的四种方式。
一、多线程的基础知识
1.1 什么是线程
线程是进程内部的一个可执行单元,是可以完成一个独立任务的顺序控制流程,如果在一个进程中同时运行多个线程,用来完成不同的工作,则称之为多线程。
1.2 何时需要多线程
(1)程序需要同时执行两个或两个以上任务时。
(2)程序需要执行一些需要等待的任务时,比如用户注册,文件读写。
(3)需要后台执行程序时。
1.3 多线程的优缺点
优点:
能适当的提高程序的执行效率
能适当提高资源利用率(cpu、内存利用率)
缺点:
开启线程需要占用一定的内存空间,(默认情况下,主线程1M,子线程512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
线程越多,cpu在调度线程上的开销就越大。
程序设计更加复杂:比如线程之间的通讯、多线程的数据共享
1.4 多线程的物种状态
- NEW:新建状态。当一个线程被创建后,启动之前,就处于该状态。
- RUNNABLE:可运行状态。当一个线程正在执行任务,就处于该状态。
- WAITING:无限等待状态。一个线程获取Lock锁对象,失败,就处于该状态。
- TIMED_WAITING:计时等待状态。当一个线程正在执行sleep方法的时候,就处于该状态。
- BLOCKED:阻塞状态。一个线程获取synchronized(代码块、方法)锁对象,失败,就处于该状态。
- TERMINATED:消亡状态。当线程把任务执行结束后,就处于该状态。
其中NEW状态和TERMINATED状态只会出现一次。
二、线程的实现方式
创建子线程就是:创建Thread类型的对象,并启动执行。
2.1 继承Thread类
** Thread类:**
所属包:java.lang;
构造方法:
public Thread();
public Thread(Runnable target);
public Thread(Runnable target,String name);
** 静态方法:**
static Thread currentThread();//获取当前线程,哪个线程执行该方法,就获取哪个线程
成员方法:
void start();//只能调用一次,不能调用多次
void run();//线程启动以后,会执行run方法中的代码
String getName();//获取线程的名称
void setName(String name);//设置线程的名称
继承Thread类创建线程的步骤如下:
- 创建一个继承于Thread类的子类。
- 重写Thread类的run方法–>将此线程执行的操作声明在run()中。
- 创建子类对象。
- 通过子类对象调用start()方法。
** 创建Thread类的对象,重写run()方法的代码如下:**
public class Test01Thread {
public static void main(String[] args) {
//以匿名内部类的方式创建Thread类型的对象,创建线程
Thread t = new Thread() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name);
}
};
t.start();//启动线程
};
}
2.2 实现Runnable接口
** Runnable接口:**
**Runnable比Thread类所具有的优势:**
1.可以避免java中的单继承的局限性(java是单继承多级继承,而实现是多实现)
2.任务和线程分离,实现了解耦合
3.线程池只能传入Runnable或者Callable类型的对象,不能使用继承的方式
** 实现Runnable接口创建线程的步骤如下:**
- 创建一个实现了Runnable接口的类
- 实现Runnable接口中的方法。
- 创建实现类的对象。
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
- 通过Thread类的对象调用start()方法。
** 实现Runnable接口创建线程的代码如下:**
public class Test02Runnable {
public static void main(String[] args) {
//采用匿名内部类的方式创建Runnable类型的对象
Runnable r = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name);
}
};
//创建线程
Thread t = new Thread(r,"线程1");
t.start();
}
}
2.3 实现Callable接口
**callable接口:**
实现Callable接口的步骤如下:
- 创建子类实现callable的实现类。
- 实现call方法,将此线程需要执行的操作声明在call方法中。
- 创建Callable接口对象
- 将此Callable实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask对象。
- 将FutureTask类的序传递到Thread类的构造器中,创建Thread类的对象,通过该对象启动线程
- 可以获取Callable实现类的call方法的返回值。(可选,需要返回值就get,不需要就不get)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Test03Callable {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//通过匿名内部类的方式创建Callable类型的对象
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "存在返回值";
}
};
//创建FutureTask对象接受callable返回值结果
FutureTask<String> ft=new FutureTask(callable);
//创建线程
Thread t=new Thread(ft);
//启动线程
t.start();
//获取线程的返回结果
String str = ft.get();
System.out.println(str);//测试Callable的返回值
}
}
2.4 线程池
** 线程池创建线程:**
**优点:**
1.降低资源消耗(减少了频繁的创建和销毁线程)。
线程池中的线程可以提前创建,而且只需完任务以后可以不销毁
2.提高响应速度。 线程池中的线程可以提前创建线程。
3.提高线程的可管理性。(任务很多,而且每个任务需要消耗的时间比较长)
创建一个线程大约需要消耗1M的空间。
**语法:**
**Executors类**
static ExecutorService newFixedThreadPool(int c);//参数是线程的数量
**ExecutorService接口:**
void execute(Runnable r);
Future<?> submit(Runnable r);//如果线程执行完任务后又返回值,则可以处理返回的结果
<T> Future<T> submit(Callable<T> c);
void shutdown();//关闭线程池
** Future<T>接口**
T get();//必须等待子线程任务执行结束后,才可以获取到返回结果。
** 实现Runnable接口,利用线程池创建线程。 **
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test04Executors {
public static void main(String[] args) {
//创建固定线程池对象,2为创建两个线程.
ExecutorService threadPool = Executors.newFixedThreadPool(2);
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
//执行任务
threadPool.execute(r);
//关闭
threadPool.shutdown();
}
}
实现Callable接口,利用线程池创建线程,并获取返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test05ExecutorsCall {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(1);
//通过匿名内部类的方式创建Callable类型的对象
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(10000);
return "有返回值";
}
};
//执行任务
Future<String> future = threadPool.submit(callable);
//获取线程的返回结果。
String str = future.get();
System.out.println(str);//测试Callable的返回值
//关闭
threadPool.shutdown();
}
}
最后,感谢大家的持续支持!!!
学而时习之,才能有所进步。
版权归原作者 飘飘~ 所有, 如有侵权,请联系我们删除。