跨境电商自建站平台,wordpress 页面顺序,天津注册公司费用,微信小程序开发要多少钱本文摘要#xff1a;【Java】Thread 线程池的 7 种创建方式及自定义线程池#xff08;代码示例版#xff09; #x1f60e; 作者介绍#xff1a;我是程序员洲洲#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专… 本文摘要【Java】Thread 线程池的 7 种创建方式及自定义线程池代码示例版 作者介绍我是程序员洲洲一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。公粽号洲与AI。 欢迎大家关注我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。 同时洲洲已经建立了程序员技术交流群如果您感兴趣可以私信我加入我的社群~社群中将不定时分享各类福利 随时欢迎您跟我沟通一起交流一起成长、进步点此即可获得联系方式~ 本文目录 前言1.线程池介绍2.线程池创建的方式2.1 ThreadPoolExecutor的详细配置2.2 Executors提供的快捷创建方法 3.线程池使用场景4.最佳代码实践4.1 ThreadPoolExecutor4.2 FixedThreadPool4.3 CachedThreadPool4.4 SingleThreadExecutor4.5 ScheduledThread4.6 SingleThreadScheduledExecutor4.7 NewWorkStealingPool 5.任务队列5.1 直接提交队列SynchronousQueue5.2 有界任务队列ArrayBlockingQueue5.3 无界任务队列LinkedBlockingQueue 5.4 优先任务队列PriorityBlockingQueue6.线程拒绝策略 前言
Java线程池是提高应用性能的关键组件。线程池通过预先创建并管理一组线程可以显著减少因频繁创建和销毁线程而产生的资源消耗。本文将探讨Java线程池的基本概念、创建方法以及最佳实践。
需要注意的是【强制】线程池不允许使用 Executors 去创建而是通过 ThreadPoolExecutor 的方式这样的处理方式让写的同学更加明确线程池的运行规则规避资源耗尽的风险。
Executors 返回的线程池对象的弊端如下 1 FixedThreadPool 和 SingleThreadPool允许的请求队列长度为 Integer.MAX_VALUE可能会堆积大量的请求从而导致 OOM。 2CachedThreadPool允许的创建线程数量为 Integer.MAX_VALUE可能会创建大量的线程从而导致 OOM。
一般情况下推荐使用 ThreadPoolExecutor 的方式进行线程池的创建因为这种创建方式更可控并且更加明确了线程池的运行规则可以规避一些未知的风险。
1.线程池介绍
线程池ThreadPool是一种资源管理策略它通过复用线程来降低资源消耗、提高响应速度并增强线程管理的可操作性。线程池预分配一定数量的线程当任务到来时线程池会分配现有线程去执行任务而不是每次都创建新的线程。
线程池的优点如下 资源节约通过复用线程减少了线程创建和销毁的开销。 性能提升任务可以快速启动因为线程已经存在。 管理增强线程池提供了更多的控制如线程数量、任务队列等。
2.线程池创建的方式
Java中创建线程池主要有两大类方法
使用ThreadPoolExecutor直接创建提供了最大的灵活性和控制力。 使用Executors工厂方法创建提供了多种快捷方式来创建常见的线程池类型。
2.1 ThreadPoolExecutor的详细配置
ThreadPoolExecutor是最灵活的线程池创建方式允许开发者自定义线程池的各项参数
核心线程数线程池中始终存活的线程数。 最大线程数线程池中允许的最大线程数。 存活时间非核心线程在没有任务执行时的存活时间。 时间单位与存活时间配合使用的时间单位。 工作队列存储等待执行任务的阻塞队列。 线程工厂用于创建新线程的工厂。 拒绝策略当任务太多无法处理时的策略。
2.2 Executors提供的快捷创建方法
Executors类提供了一些快捷方法来创建特定类型的线程池
FixedThreadPool固定大小的线程池。 CachedThreadPool可缓存的线程池会根据需要创建新线程。 SingleThreadExecutor单个线程的线程池保证任务顺序执行。 ScheduledThreadPool可以执行定时任务的线程池。 WorkStealingPoolJDK 1.8新增任务被多个线程池线程抢占执行。
3.线程池使用场景
FixedThreadPool适用于需要固定数量线程执行任务的场景。 CachedThreadPool适合处理大量短期异步任务。 SingleThreadExecutor保证任务按照提交的顺序执行。 ScheduledThreadPool适合需要定时或周期性执行任务的场景。
4.最佳代码实践
4.1 ThreadPoolExecutor
public class ThreadPoolExecutorTest {public static void main(String[] args) {ThreadPoolExecutor threadPool new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue(10));// 执行任务for (int i 0; i 10; i) {final int index i;threadPool.execute(() - {System.out.println(index 被执行,线程名: Thread.currentThread().getName());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}});}}
}可以设置7个参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {}corePoolSize核心线程数线程池中始终存活的线程数。maximumPoolSize最大线程数线程池中允许的最大线程数当线程池的任务队列满了之后可以创建的最大线程数。keepAliveTime最大线程数可以存活的时间当线程中没有任务执行时最大线程就会销毁一部分最终保持核心线程数量的线程。unit单位是和参数 3 存活时间配合使用的合在一起用于设定线程的存活时间。
TimeUnit.DAYS天 TimeUnit.HOURS小时 TimeUnit.MINUTES分 TimeUnit.SECONDS秒 TimeUnit.MILLISECONDS毫秒 TimeUnit.MICROSECONDS微妙 TimeUnit.NANOSECONDS纳秒
workQueue一个阻塞队列用来存储线程池等待执行的任务均为线程安全。它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种
ArrayBlockingQueue一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue一个由链表结构组成的有界阻塞队列。 SynchronousQueue一个不存储元素的阻塞队列即直接提交给线程不保持它们。 PriorityBlockingQueue一个支持优先级排序的无界阻塞队列。 DelayQueue一个使用优先级队列实现的无界阻塞队列只有在延迟期满时才能从中提取元素 LinkedTransferQueue一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似还含有非阻塞方法。 LinkedBlockingDeque一个由链表结构组成的双向阻塞队列。 较常用的是 LinkedBlockingQueue 和 Synchronous线程池的排队策略与 BlockingQueue 有关
threadFactory线程工厂主要用来创建线程。handler拒绝策略拒绝处理任务时的策略系统提供了 4 种。
AbortPolicy拒绝并抛出异常。默认策略 CallerRunsPolicy使用当前调用的线程来执行此任务。 DiscardOldestPolicy抛弃队列头部最旧的一个任务并执行当前任务。 DiscardPolicy忽略并抛弃当前任务。
ThreadPoolExecutor 关键节点的执行流程如下
1、当线程数小于核心线程数时创建线程。 2、当线程数大于等于核心线程数且任务队列未满时将任务放入任务队列。 3、当线程数大于等于核心线程数且任务队列已满若线程数小于最大线程数创建线程若线程数等于最大线程数抛出异常拒绝任务。
4.2 FixedThreadPool
FixedThreadPool创建一个固定大小的线程池可控制并发的线程数超出的线程会在队列中等待。
一般用于Web 服务瞬时削峰但需注意长时间持续高峰情况造成的队列阻塞。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable());
}corePoolSize 与 maximumPoolSize 相等即其线程全为核心线程是一个固定大小的线程池是其优势。keepAliveTime 0 该参数默认对核心线程无效而 FixedThreadPool 全部为核心线程。workQueue 为 LinkedBlockingQueue无界阻塞队列队列最大值为 Integer.MAX_VALUE。如果任务提交速度持续大于任务处理速度会造成队列大量阻塞。因为队列很大很有可能在拒绝策略前内存溢出。是其劣势FixedThreadPool 的任务执行是无序的。
public class NewFixedThreadPoolTest {public static void main(String[] args) {System.out.println(主线程启动);// 1.创建1个有2个线程的线程池ExecutorService threadPool Executors.newFixedThreadPool(2);Runnable runnable new Runnable() {Overridepublic void run() {try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}System.out.println(任务被执行线程 Thread.currentThread().getName());}};// 2.线程池执行任务添加4个任务每次执行2个任务得执行两次threadPool.submit(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);System.out.println(主线程结束);}
}线程池通过复用一组固定数量的线程来执行多个任务这些线程在一个共享的无界队列上操作。在任一时刻至多有 nThreads 个线程在积极地处理任务。如果所有线程都忙碌且此时有新任务提交那么这些新任务将被放入队列中排队直到有线程空闲出来。
该线程池能够同时处理两个任务因为有两个活跃的线程。如果这两名线程都在执行任务那么新提交的两个任务将进入等待队列直到这两个线程中的任何一个完成其当前任务。
在Java的线程池中submit() 和 execute() 是两种不同的方法它们都用于向线程池提交任务。submit() 方法允许你提交一个任务并返回一个 Future 对象这个对象可以用来查询任务状态、取消任务或获取任务执行结果。相比之下execute() 方法用于提交一个任务以供执行但它不返回任何表示任务的 Future 对象。
4.3 CachedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class CachedThreadPoolExample {public static void main(String[] args) {// 创建 CachedThreadPool 线程池ExecutorService threadPool Executors.newCachedThreadPool();// 提交任务到线程池for (int i 0; i 10; i) {final int taskNumber i;threadPool.execute(() - {System.out.println(执行任务 taskNumber 由线程 Thread.currentThread().getName() 处理);try {TimeUnit.SECONDS.sleep(1); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池不再接受新任务threadPool.shutdown();}
}CachedThreadPool 是通过Executors.newCachedThreadPool() 方法创建的它是一种可扩展的线程池核心线程数为 0最大线程数为 Integer.MAX_VALUE。该线程池适合用于执行大量短期异步任务。它在需要时会动态创建新线程如果线程空闲时间超过 60 秒则会被终止并从线程池中移除。CachedThreadPool 使用的是一个同步队列 SynchronousQueue 作为工作队列这个队列没有容量即它不会存储提交的任务而是直接将任务交给线程执行。在示例代码中我们创建了一个 CachedThreadPool 并提交了 10 个任务。每个任务简单地打印出它正在被哪个线程执行并模拟执行时间。最后我们调用 shutdown() 方法来关闭线程池使其不再接受新任务。注意这不会立即停止所有正在执行的任务而是等待它们完成后线程池才会完全关闭。
使用 CachedThreadPool 时需要注意由于其最大线程数可以非常大如果任务提交得非常快可能会导致创建大量线程从而耗尽系统资源。因此应当谨慎使用并确保任务执行不会过快或者考虑设置适当的线程池参数。
4.4 SingleThreadExecutor
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class SingleThreadExecutorExample {public static void main(String[] args) {// 创建 SingleThreadExecutor 线程池ExecutorService threadPool Executors.newSingleThreadExecutor();// 提交多个任务到单线程线程池for (int i 1; i 5; i) {final int taskNumber i;threadPool.execute(() - {System.out.println(执行任务 taskNumber 由线程 Thread.currentThread().getName() 处理);try {TimeUnit.SECONDS.sleep(2); // 模拟耗时任务} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断状态System.out.println(任务 taskNumber 被中断);}});}// 优雅关闭线程池等待所有任务执行完毕threadPool.shutdown();try {// 等待线程池关闭或者超时if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {// 超时后强制关闭线程池threadPool.shutdownNow();}} catch (InterruptedException e) {threadPool.shutdownNow(); // 捕获中断异常时立即关闭线程池Thread.currentThread().interrupt(); // 重新设置中断状态}}
}SingleThreadExecutor 是通过 Executors.newSingleThreadExecutor() 方法创建的它确保所有的任务都在同一个线程中按顺序执行。
这种类型的线程池内部其实只有一个线程在工作也就是单线程环境它保证了任务的执行顺序即先提交的任务先执行。
在示例代码中我们创建了一个 SingleThreadExecutor 并提交了 5 个任务。每个任务简单地打印出它正在被哪个线程执行并模拟执行时间。
我们使用了 shutdown() 方法来开始关闭线程池的过程这将阻止线程池接受新任务但会等待已提交的任务执行完毕。
awaitTermination 方法用来等待线程池中的所有任务执行完成或者直到超时。如果超时则会调用 shutdownNow() 方法尝试立即停止所有正在执行的任务。 SingleThreadExecutor 适用于任务不需要并发执行并且希望按照特定顺序执行的场景。
4.5 ScheduledThread
public class ScheduledThreadTest {public static void main(String[] args) {ScheduledExecutorService threadPool Executors.newScheduledThreadPool(5);System.out.println(添加任务时间 new Date());threadPool.schedule(() - {System.out.println(任务被执行时间 new Date());}, 2, TimeUnit.SECONDS);}
}4.6 SingleThreadScheduledExecutor
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class SingleThreadScheduledExecutorExample {public static void main(String[] args) {// 创建 SingleThreadScheduledExecutor 线程池ScheduledExecutorService scheduledExecutorService Executors.newSingleThreadScheduledExecutor();// 提交一个任务延迟2秒后执行scheduledExecutorService.schedule(() - {System.out.println(任务在 TimeUnit.SECONDS.convert(System.nanoTime(), TimeUnit.NANOSECONDS) 秒后执行);}, 2, TimeUnit.SECONDS);// 重复执行的任务每隔2秒执行一次初始延迟2秒scheduledExecutorService.scheduleAtFixedRate(() - {System.out.println(周期性任务执行时间 new java.util.Date());}, 2, 2, TimeUnit.SECONDS);// 线程池不会立即关闭因为任务还在执行// 可以安排关闭线程池的逻辑例如使用 ScheduledExecutorService 的 shutdown 方法}
}SingleThreadScheduledExecutor 是通过 Executors.newSingleThreadScheduledExecutor() 方法创建的它是一个单线程执行定时任务的线程池。
与SingleThreadExecutor不同SingleThreadScheduledExecutor支持定时任务和周期性任务的执行。
在示例代码中我们首先使用schedule方法提交了一个延迟2秒后执行的单次任务。
然后我们使用scheduleAtFixedRate方法提交了一个周期性任务该任务每隔2秒执行一次并且有一个初始延迟2秒。
SingleThreadScheduledExecutor 保证所有任务都在同一个线程中顺序执行这对于需要保证任务执行顺序的场景非常有用。
由于SingleThreadScheduledExecutor是为定时任务设计的所以它不会像shutdown方法那样立即关闭线程池。如果需要关闭线程池应该在所有任务执行完毕后调用shutdown方法并妥善处理关闭逻辑。
此类型的线程池适用于执行定时任务和周期性任务如定期的数据备份、定时检查等场景。
4.7 NewWorkStealingPool
NewWorkStealingPool创建一个抢占式执行的线程池任务执行顺序不确定任务的执行顺序是不确定的注意此方法只有在 JDK 1.8 版本中才能使用。
public class NewWorkStealingPoolTest {public static void main(String[] args) {ExecutorService threadPool Executors.newWorkStealingPool();for (int i 0; i 10; i) {final int index i;threadPool.execute(() - {System.out.println(index 被执行,线程名: Thread.currentThread().getName());});}// 确保任务执行完成while (!threadPool.isTerminated()) {}}
}5.任务队列
5.1 直接提交队列SynchronousQueue
直接提交队列不存储任务每个提交的任务必须立即由线程池中的某个线程接收并开始执行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;public class DirectSubmissionQueueExample {public static void main(String[] args) {// 使用直接提交队列SynchronousQueueRunnable queue new SynchronousQueue();ThreadPoolExecutor executor new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, queue);// 提交任务executor.execute(() - {System.out.println(任务被执行线程 Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});// 关闭线程池executor.shutdown();}
}在这个示例中我们创建了一个核心线程数和最大线程数都为1的ThreadPoolExecutor使用了SynchronousQueue作为任务队列。任务一提交就会尝试执行因为没有存储机制所以任务不会被缓存。
5.2 有界任务队列ArrayBlockingQueue
有界任务队列可以存储有限数量的任务。当队列满了之后根据拒绝策略处理新提交的任务。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;public class BoundedQueueExample {public static void main(String[] args) {// 创建有界任务队列ArrayBlockingQueueRunnable queue new ArrayBlockingQueue(2);ThreadPoolExecutor executor new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务for (int i 0; i 3; i) {executor.execute(() - {System.out.println(任务 (i 1) 被执行线程 Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}在这个示例中我们创建了一个有界队列ArrayBlockingQueue容量为2。这意味着它最多可以存储两个任务。当提交的任务超过这个数量时根据设置的拒绝策略处理。
5.3 无界任务队列LinkedBlockingQueue
无界任务队列可以存储无限数量的任务直到内存限制。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;public class UnboundedQueueExample {public static void main(String[] args) {// 创建无界任务队列LinkedBlockingQueueRunnable queue new LinkedBlockingQueue();ThreadPoolExecutor executor new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务for (int i 0; i 10; i) {executor.execute(() - {System.out.println(任务 (i 1) 被执行线程 Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}在这个示例中我们创建了一个无界队列LinkedBlockingQueue。这意味着它可以存储任意数量的任务直到系统内存耗尽。
5.4 优先任务队列PriorityBlockingQueue
优先任务队列可以根据任务的优先级来执行任务。
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;public class PriorityQueueExample {public static void main(String[] args) {// 创建优先任务队列PriorityBlockingQueueRunnable queue new PriorityBlockingQueue();ThreadPoolExecutor executor new ThreadPoolExecutor(1, 2, 0, TimeUnit.MILLISECONDS, queue);// 提交任务可以定义任务优先级executor.execute(new PrioritizedTask(5, 任务5));executor.execute(new PrioritizedTask(1, 任务1));executor.execute(new PrioritizedTask(10, 任务10));// 关闭线程池executor.shutdown();}static class PrioritizedTask implements Runnable, ComparablePrioritizedTask {private int priority;private String taskName;public PrioritizedTask(int priority, String taskName) {this.priority priority;this.taskName taskName;}Overridepublic void run() {System.out.println(taskName 被执行线程 Thread.currentThread().getName());}Overridepublic int compareTo(PrioritizedTask other) {return Integer.compare(this.priority, other.priority); // 升序排序}}
}在这个示例中我们创建了一个PriorityBlockingQueue它根据任务的优先级来排序任务。我们定义了一个PrioritizedTask类实现了Runnable和Comparable接口以支持优先级排序。任务将根据它们的优先级被执行。
6.线程拒绝策略
我们来演示一下 ThreadPoolExecutor 的拒绝策略的触发我们使用 DiscardPolicy 的拒绝策略它会忽略并抛弃当前任务的策略实现代码如下
public class ThreadPoolStrategyTest {public static void main(String[] args) {// 线程池ThreadPoolExecutor threadPool new ThreadPoolExecutor(1, 1, 100,TimeUnit.SECONDS, new LinkedBlockingQueue(1), new ThreadPoolExecutor.DiscardPolicy());// 任务Runnable runnable new Runnable() {Overridepublic void run() {System.out.println(当前任务被执行,执行时间: new Date() 执行线程: Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}};// 开启4个任务threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);}
}除了 Java 自身提供的 4 种拒绝策略之外我们也可以自定义拒绝策略示例代码如下
public class MyThreadPoolStrategyTest {public static void main(String[] args) {Runnable runnable new Runnable() {Overridepublic void run() {System.out.println(当前任务被执行,执行时间: new Date() 执行线程: Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}}};ThreadPoolExecutor threadPool new ThreadPoolExecutor(1, 1, 100,TimeUnit.SECONDS, new LinkedBlockingQueue(1), new RejectedExecutionHandler() {Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 执行自定义拒绝策略的相关操作System.out.println(我是自定义拒绝策略~);}});threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);}
}以下是自定义线程池使用了有界队列自定义 ThreadFactory 和拒绝策略的demo
public class MyThreadPoolTest {public static void main(String[] args) throws Exception {BlockingQueueRunnable workQueue new ArrayBlockingQueue(2);NameThreadFactory threadFactory new NameThreadFactory();RejectedExecutionHandler handler new MyIgnorePolicy();ThreadPoolExecutor executor new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS,workQueue, threadFactory, handler);// 预启动所有核心线程executor.prestartAllCoreThreads();for (int i 1; i 10; i) {MyTask task new MyTask(String.valueOf(i));executor.execute(task);}//阻塞主线程System.in.read();}static class NameThreadFactory implements ThreadFactory {private final AtomicInteger mThreadNum new AtomicInteger(1);Overridepublic Thread newThread(Runnable r) {Thread t new Thread(r, my-thread- mThreadNum.getAndIncrement());System.out.println(t.getName() has been created);return t;}}static class MyIgnorePolicy implements RejectedExecutionHandler {Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {doLog(r, executor);}private void doLog(Runnable r, ThreadPoolExecutor e) {// 可做日志记录等System.err.println( r.toString() rejected);System.out.println(completedTaskCount: e.getCompletedTaskCount());}}static class MyTask implements Runnable {private String name;public MyTask(String name) {this.name name;}Overridepublic void run() {try {System.out.println(this.toString() is running!);// 让任务执行慢点Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}public String getName() {return name;}Overridepublic String toString() {return MyTask [name name ];}}
}