三种创建线程的方法
一、通过继承 Thread 类本身
public class MyThread extends Thread {
@Override
public void run(){
super.run();
System.out.println("执行子线程...");
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("主线程...");
}
}
二、通过实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("执行子线程...");
}
}
public class Test {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("主线程运行结束!");
}
}
三、通过 Callable 和 Future 创建线程
public class MyCallable implements Callable {
int i = 0;
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+" i的值:"+ i);
return i++; //call方法可以有返回值
}
}
public class Test {
public static void main(String[] args) {
Callable callable = new MyCallable();
for (int i = 0; i < 10; i++) {
FutureTask task = new FutureTask(callable);
new Thread(task,"子线程"+ i).start();
try {
//获取子线程的返回值
System.out.println("子线程返回值:"+task.get() + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
第一种和后面两种的对比:1、 通过代码可以看出,第一种方法是最简洁方便的,直接就可以start,不需要任何转换 2、但是第一种有一个很不好的地方就是继承了Thread类后由于java的单继承机制,就不 可以继承其他的类了,而如果实现的是接口,就可以实现多个接口,使开发更灵活
第二种和第三种方式对比:1、同样的,第二种方法相对第三种方式来说代码更简洁,使用更方便,少了一次转换 2、第三种方法有两个优点:有返回值、可以抛出异常
总结:实际开发中可能有更复杂的代码实现,需要继承其他的类,所以平时更推荐通过实现接 口来实现多线程,也就是通过第二或第三种方式来实现,这样能保持代码灵活和解耦。 而选择第二还是第三种方式,则要根据run()方法是不是需要返回值或者捕获异常来决定, 如果不需要,可以选择用第二种方式实现,代码更简洁。
线程池
上述常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口。但是我们创建这两种线程在运行结束后都会被虚拟机销毁,如果线程数量多的话,频繁的创建和销毁线程会大大浪费时间和效率,更重要的是浪费内存,因为正常来说线程执行完毕后死亡,线程对象变成垃圾!那么有没有一种方法能让线程运行完后不立即销毁,而是让线程重复使用,继续执行其他的任务哪?我们使用线程池就能很好地解决这个问题。
-
减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
-
可以根据系统的承受能力,调整线程池中工作线程的数目,放置因为消耗过多的内存,而把服务器累趴下(每个线程大约需要 1 MB 内存,线程开的越多,消耗的内存也就越大,最后死机)
使用Executors类中提供的几个静态方法来创建线程池,内部实现均使用了 ThreadPoolExecutor 实现,其实都只是ThreadPoolExecutor 类的封装:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
函数的参数含义如下:
- corePoolSize:指定了线程池中的线程数量
- maximumPoolSize:指定了线程池中的最大线程数量
- keepAliveTime:当线程池线程数量超过 corePoolSize 时,多余的空闲线程的存活时间。即,超过了 corePoolSize 的空闲线程,在多长时间内,会被销毁。
- unit: keepAliveTime 的单位。
- workQueue:任务队列,被提交但尚未被执行的任务。
- threadFactory:线程工厂,用于创建线程,一般用默认的即可。
- handler:拒绝策略。当任务太多来不及处理,如何拒绝任务。
线程池内部维护的工作者线程的数量就是该线程池的线程池大小,有 3 种形态:
-
如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;
-
如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;
-
如果无法将请求加入队列,即队列已经满了,则创建新的线程,除非创建此线程超出 maxinumPoolSize, 在这种情况下,任务将被拒绝。(拒绝后的处理有四种,如下图)
工作线程数量可以理解为高速上的入口通道,比如高速入口共有8个通道,正常开放4个(corePoolSize),如果只有三辆车,每辆车都可以分别走一个通道,如果有五辆车,这第五辆车排队等待,而不是加开一个通道。如果车辆很多导致排队队伍过长,则需要加开通道直到所有8个通道(maxinumPoolSize)全开。如果通道全开且排队队伍已满,则关闭高速入口,后面的车辆需要绕行(任务将被拒绝)。
线程池创建示例:
- Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
- Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
- Executors.newFixedThreadPool(int maximumPoolSize); //创建固定容量大小的缓冲池
- Executors.newScheduledThreadPool(int corePoolSize); // 固定线程数,支持定时和周期性任务
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
ExecutorService cachePool = Executors.newCachedThreadPool();
ExecutorService singlePool = Executors.newSingleThreadExecutor();
ExecutorService fixedPool = Executors.newFixedThreadPool(int);
ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
ExecutorService myExecutor = new ThreadPoolExecutor(4, 5, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1000));
自定义的线程名
//自定义线程名称
static class DIYThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DIYThreadFactory(String diyName) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = diyName +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
private static ExecutorService myExecutor = Executors.newFixedThreadPool(10,new DIYThreadFactory("XXX"));