方式1:继承Thread类
步骤:
- 创建一个继承于Thread类的子类
- 重写Thread的run()方法
- 创建当前Thread子类的对象
- 通过实例对象调用start()方法,启动线程----》JAVA虚拟机会调用run()方法
实现:
public class TestMyThread {public static void main(String[] args) {//创建自定义线程对象1MyThread mt1 = new MyThread("子线程1");//开启子线程1mt1.start();//创建自定义线程对象2,分别创建对象开启线程,不可以数据共享,若要共享需要创建static变量MyThread mt2 = new MyThread("子线程2");//开启子线程2mt2.start();}
}
方式2:实现Runnable接口
步骤:
- 创建一个实现Runnable接口的类
- 实现接口中的run()方法
- 创建实例对象
- 将此对象作为参数传到Thread类的构造器中,创建Thread类的实例
- 通过Thread的实例对象调用start()方法,启用线程—》JAVA虚拟机会调用run()方法
实现:
public class TestMyRunnable {public static void main(String[] args) {//创建自定义类对象 线程任务对象MyRunnable mr = new MyRunnable();//创建线程对象1Thread 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()中@Overridepublic 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{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread1 implements Runnable{@Overridepublic void run() {for(int i = 0;i <= 100;i++){if(i % 2 != 0){System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}class NumberThread2 implements Callable {@Overridepublic 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());//ThreadPoolExecutorservice1.setMaximumPoolSize(50); //设置线程池中线程数的上限//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象service.execute(new NumberThread());//适合适用于Runnableservice.execute(new NumberThread1());//适合适用于Runnabletry {Future future = service.submit(new NumberThread2());//适合使用于CallableSystem.out.println("总和为:" + future.get());} catch (Exception e) {e.printStackTrace();}//3.关闭连接池service.shutdown();}}