多线程的实现方式
自定义类继承thread类
重写run()方法,创建自定义类对象,调用start()方法
优缺点: 开多个线程需要new多个对象,每一个类的成员变量都一样,需要在栈中开辟很多内存
自定义类实现runnable接口
重写run()方法, 创建自定义类对象, 创建thread对象,使用自定义类多少作为构造参数,调用start()方法
优缺点: 自定义一个对象,new多个thread对象
为什么要重写run()方法?
run()方法里面封装的是线程执行的代码。
run()方法和start()方法的区别?
run()方法直接调用仅仅是普通方法
start()方法是先启动一个新的线程,再有jvm去调用run()方法
Executors创建线程池
Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。
newFixedThreadPool(int nThreads) 创建固定大小的线程池
newSingleThreadExecutor() 创建只有一个线程的线程池
newCachedThreadPool() 创建一个不限线程数上限的线程池,任何提交的任务都将立即执行
小程序使用这些快捷方法没什么问题,对于服务端需要长期运行的程序,创建线程池应该直接使用ThreadPoolExecutor的构造方法
// Java线程池的完整构造函数
publicThreadPoolExecutor(
intcorePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
intmaximumPoolSize, // 线程数的上限
longkeepAliveTime, TimeUnit unit, // 超过corePoolSize的线程的idle时长,
// 超过这个时间,多余的线程会被回收。
BlockingQueue workQueue, // 任务的排队队列
ThreadFactory threadFactory, // 新线程的产生方式
RejectedExecutionHandler handler)// 拒绝策略
corePoolSize和maximumPoolSize设置不当会影响效率,甚至耗尽线程;
workQueue设置不当容易导致OOM;
handler设置不当会导致提交任务时抛出异常。
可以向线程池提交的任务有两种:Runnable和Callable
Callable允许有返回值,允许抛出异常