方式1:继承Thread类
步骤:
- 创建一个继承于Thread类的子类
- 重写Thread的run()方法
- 创建当前Thread子类的对象
- 通过实例对象调用start()方法,启动线程----》JAVA虚拟机会调用run()方法
实现:
public class TestMyThread {
public static void main(String[] args) {
//创建自定义线程对象1
MyThread mt1 = new MyThread("子线程1");
//开启子线程1
mt1.start();
//创建自定义线程对象2,分别创建对象开启线程,不可以数据共享,若要共享需要创建static变量
MyThread mt2 = new MyThread("子线程2");
//开启子线程2
mt2.start();
}
}
方式2:实现Runnable接口
步骤:
- 创建一个实现Runnable接口的类
- 实现接口中的run()方法
- 创建实例对象
- 将此对象作为参数传到Thread类的构造器中,创建Thread类的实例
- 通过Thread的实例对象调用start()方法,启用线程—》JAVA虚拟机会调用run()方法
实现:
public class TestMyRunnable {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象1
Thread t1 = new Thread(mr, "长江1");
t1.start();
//创建线程对象2,注意两个线程传入的是同一个对象,可以实现数据共享
Thread t2 = new Thread(mr, "长江2");
t2.start();
}
}
方式3:实现Callable接口
- 与使用Runnable相比,Callable功能更强大些。
- 相比run()方法,call()可以有返回值,更灵活。
- call()方法可以使用throws抛出异常,更灵活
- Callable使用泛型参数,可以指明具体的call()返回值类型,更灵活。
- Future接口
- 可以对具体Runnable,Callable任务的执行接口进行取消,查询是否完成,获取结果等
- FutureTask是Future接口的唯一的实现类
- FutureTask同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
- 缺点:在获取分线程执行结果的时候,当前线程或主线程受阻塞,效率较低。
实现:
/*
* 创建多线程的方式三:实现Callable (jdk5.0新增的)
*/
//1.创建一个实现Callable的实现类
class NumThread implements Callable {
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class CallableTest {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//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();
}
}
}
方式4: 使用线程池
背景:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用。
好处:
- 提高了程序执行的效率(线程已经提前创建好了)
- 提高了资源复用率(执行完的线程并未销毁,还可以继续执行其他任务,不需要每次都创建)
- 可以设置相关参数,对线程池中的线程进行管理
corePoolSize: 核心池的大小
maximumPoolSize: 最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止。多余空闲线程的存活时间。当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余空闲线程会被销毁直到剩下corePoolSize为止。
unit: keepAliveTime的单位
workQueue: 里面放了被提交但是尚未执行的任务
threadFactory: 表示线程池中工作线程的线程工厂,用于创建线程
handler: 拒绝策略,当队列满了并且工作线程大于等于线程池的最大线程数时,对任务的拒绝方式。
四种拒绝策略
- AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常进行
- CallerRunsPolicy:调用者运行一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
- DiscardPolicy: 改变略默默丢弃无法处理的任务,不予任何受理也不抛出异常,如果允许任务丢弃,这是最好的一种策略
- DiscardOldestPolicy: 抛弃队列中等待最久的任务,然后将当前任务加入队列,然后再次提交任务
ExecutorService: 真正的线程池接口,常见子类ThreadPoolExecutor
void execute(Runnable command): 执行任务命令,没有返回值,一般用来执行Runnable
Future submit(Callable task): 执行任务,有返回值,一般用来执行Callable
void shutdown(): 关闭连接池
- Executors: 一个线程池的工厂类,通过此类的静态工厂方法可以创建多种类型的线程池对象。
Executors.newCachedThreadPool(): 创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(int nThreads): 创建一个可重用固定线程数的线程池
Executors.newSingleThreadPool(): 创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(int corePoolSize): 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
实现:
// 这里使用实现Runnable的方式
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread2 implements Callable {
@Override
public Object call() throws Exception {
int evenSum = 0;//记录偶数的和
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
evenSum += i;
}
}
return evenSum;
}
}
public class ThreadPoolTest {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
// //设置线程池的属性
// System.out.println(service.getClass());//ThreadPoolExecutor
service1.setMaximumPoolSize(50); //设置线程池中线程数的上限
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
try {
Future future = service.submit(new NumberThread2());//适合使用于Callable
System.out.println("总和为:" + future.get());
} catch (Exception e) {
e.printStackTrace();
}
//3.关闭连接池
service.shutdown();
}
}