Java中创建线程的方法大全
下文笔者讲述创建线程的四种方法分享,如下所示
java中创建线程有以下四种方式: 方式1:继承 Thread 类,重写 run() 方法 方式2:实现 Runnable 接口,实现 run() 方法, 并将 Runnable 实现类的实例作为Thread 构造函数的参数 target 方式3:实现 Callable 接口,实现 call() 方法, 然后通过 FutureTask 包装器来创建 Thread 线程 方式4:通过 ThreadPoolExecutor 创建线程池, 并从线程池中获取线程用于执行任务
方式1:继承Thread类
创建步骤: 定义类继承Thread 重写Thread类中的run方法 实例化线程对象 调用线程的start方法开启线程
package com.java265; // 1.继承Thread类 public class ThreadDemo extends Thread{ private int ticket=20; // 2.重写run 方法 public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("剩余票数=="+ticket--); } } } } class ThreadTest { public static void main(String[] args) { // 3.实例化线程对象 ThreadDemo md = new ThreadDemo() ; // 4.调用start()开启线程 md.start(); } }
实现Runnable接口的方式创建线程
创建步骤: 实现Runnable接口 重写Run()方法 实例化实现类 将实例化线程类转化为线程对象 开启线程 调用start()方法例
package com.java265; // 1.实现Runnable类 public class RunnableDemo implements Runnable{ private int ticket=20; // 2.重写run 方法 public void run(){ for(int i=0;i<20;i++){ if(this.ticket>0){ System.out.println("剩余票数=="+ticket--); } } } } class RunnableTest { public static void main(String[] args) { // 3.实例化实现类 RunnableDemo rd = new RunnableDemo() ; // 4.将实例化实现类转化为线程对象 Thread thread = new Thread(rd); // 5.开启线程 thread.start(); } }
实现Runnable接口比继承 Thread 类所具有的优势主要有: 可以避免JAVA中单继承的限制; 线程池只能放入实现Runable或Callable类线程 不能直接放入继承Thread类 代码可以被多个线程共享 代码和数据独立 适合多个相同的程序代码的线程去处理同一个资源的情况
实现Callable接口
实现步骤 1、定义一个线程任务类实现Callable接口,声明线程执行的结果类型 2、重写线程任务类的call()方法,这个方法可以直接返回执行的结果 3、创建一个Callable的线程任务对象 4、把Callable的线程任务对象包装成一个未来任务对象 5、把未来任务对象包装成线程对象 6、调用线程start()方法,启动线程 7、获取线程执行结果例
// 1、定义一个线程任务类实现Callable接口,声明线程执行的结果类型。 public class CallableTicket implements Callable<Object > { private int ticket=20; // 2、重写线程任务类的call()方法,这个方法可以直接返回执行的结果。 @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { sum += i; } } return sum; } } package com.java265; import java.util.concurrent.FutureTask; public class CallableMain { public static void main(String[] args) { // 3、创建一个Callable的线程任务对象。 CallableTicket callableTicket = new CallableTicket(); // 4、把Callable的线程任务对象包装成一个未来任务对象。 FutureTask futureTask = new FutureTask(callableTicket); // 5、把未来任务对象包装成线程对象。 Thread thread = new Thread(futureTask); // 6、调用线程start()方法,启动线程。 thread.start(); // 7、获取线程执行结果。如果此时获取结果的任务还未执行完成,会让出CPU,直至任务执行完成才获取结果。 try { //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()方法返回值。 Object sum = futureTask.get(); System.out.println("总和为:"+sum); } catch (Exception e) { e.printStackTrace(); } } }
此方式的优点: 与使用Runnable相比,Callable功能更强大 相比run方法,可以有返回值 方法可以抛异常 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果 Future接口 可以对具体Runnable、Callable任务的运行结果 进行取消、查询是否完成、获取结果等。 FutureTask是Future接口的唯一实现类。 FutureTask同时实现了Runable,Future接口 它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
使用线程池
通过Executors创建 它提供了四种线程池: newCachedThreadPool创建一个可缓存线程池 当线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池 可控制线程最大并发数,超出的线程会在队列中等待。 newScheduledThreadPool 创建一个定长线程池 支持定时及周期性任务执行 newSingleThreadExecutor 创建一个单线程化的线程池 它只会用唯一的工作线程来执行任务 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行例
public class Demo01 { public static void main(String[] args) { //ExecutorService threadPool = Executors.newSingleThreadExecutor(); //创建单个线程 //ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);//创建一个固定大小的线程池 //ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建一个固定大小的线程池 ExecutorService threadPool = Executors.newCachedThreadPool(); //创建大小可伸缩的线程池 try { for (int i = 0; i < 30; i++) { //使用线程池来创建线程 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); //线程池使用完毕后需要关闭 } } }
注意事项: 不建议使用这种方法创建线程池 因为newFixedThreadPool 和newSingleThreadExecutor允许的最大请求队列长度为Integer.MAX_VALUE 可能会堆积大量的请求 从而导致OOM newCachedThreadPoo和newScheduledThreadPool允许的创建线程的最大数量为Integer.MAX_VALUE 从而导致OOM。 使用ThreadPoolExecutor创建 ThreadPoolExecutor有7个核心参数 ThreadPoolExecutor(int corePoolSize, //核心线程池大小,始终存在 int maximumPoolSize, //最大线程数 long keepAliveTime, //空闲线程等待时间,超时则销毁 TimeUnit unit, //时间单位 BlockingQueue<Runnable> workQueue, //等待阻塞队列 ThreadFactory threadFactory, //线程工厂 RejectedExecutionHandler handler) //线程拒绝策略 其最后一个参数拒绝策略共有四种: new ThreadPoolExecutor.AbortPolicy():达到最大承载量,不再处理,并且抛出异常 new ThreadPoolExecutor.CallerRunsPolicy():达到最大承载量,从哪来的去哪里 new ThreadPoolExecutor.DiscardPolicy():达到最大承载量,丢掉任务,但不抛出异常 new ThreadPoolExecutor.DiscardOldestPolicy():达到最大承载量,尝试与最早执行的线程去竞争,不抛出异常例
public class TestClass { public static void main(String[] args) { //new ThreadPoolExecutor.AbortPolicy():达到最大承载量,不再处理,并且抛出异常 //new ThreadPoolExecutor.CallerRunsPolicy():达到最大承载量,从哪来的去哪里 //new ThreadPoolExecutor.DiscardPolicy():达到最大承载量,丢掉任务,但不抛出异常 //new ThreadPoolExecutor.DiscardOldestPolicy():达到最大承载量,尝试与最早执行的线程去竞争,不抛出异常 //最大线程池大小该如何定义 //1.cpu密集型,逻辑处理器个数 //2.io密集型 > 判断程序十分耗IO的线程,最大线程池大小应该比这个大 int maxPools= Runtime.getRuntime().availableProcessors(); System.out.println(maxPools); ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 2, maxPools, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() ); try { for (int i = 0; i < 10; i++) { //使用线程池来创建线程 //最大承载:maximumPoolSize+workQueue,超过执行拒绝策略 threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); //线程池使用完毕后需要关闭 } } }
使用线程池的优点; 1.减少资源的消耗。 重复使用已经创建的线程,避免频繁的创造和销毁线程,减少消耗 2.提高响应速度。 当执行任务时,不需要去创建线程再来执行, 只要调动现有的线程来运行即可 3.提高了线程的管理性。线程是稀缺资源, 使用线程池可以进行统一的分配、调优和监控
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。