定义任务的方式 Runnable接口
一个类只要实现了Runnable接口,将任务写在重写的run方法中,这就是定义了一个任务,看代码:
// Demo.java
//一个任务
static class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println("hi");
}
}
// main
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();
runnableDemo.run();
}
定义了一个静态的RunnableDemo类,实现了Runnable接口,这样就将一个任务描述好了。
任务描述好了之后,需要将它交给线程去执行,这里是在main()的线程去调用的。
如果需要由单独的线程进行驱动,则需要Thread构造器。
Thread构造器,另起一个线程
Thread thread = new Thread(Runnable target);
我们只需要往里放一个Runnable对象即可,这是Thread其中一个构造函数。Runnable就是我们定义的任务。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
所以使用Thread有两种方式:
- 事先用类实现
Runnable接口定义任务 - 使用Thread构造器,采用匿名类的方式,在构造参数中使用
lambda表达式 或者 匿名类的方式
Thread thread = new Thread(() -> {
// 业务逻辑
});
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 业务逻辑
}
});
Thread构造器的对象,调用其start()方法,为线程执行必需的初始化操作,然后会调用Runnable的run()方法。
忽略线程组
也许你在其他文章中或者Thread构造器中,看见了线程组的概念。这里引用Java编程思想中的一句话:“最好把线程组看成是一次不成功的尝试,你只要忽略它就好了”。
接下去我们要了解一下新的管理线程的对象:Executor。
Executor 线程池
操作数据库连接的时候,如果手动管理一个连接需要手动的连接和关闭,这是需要很多开销时间的。这对并发管理线程是一样的道理,手动创建的Thread对象是没有线程池这个概念的,而且构造方式也表明了一个Thread对象只能操作一个任务。,所以Executor简化了并发编程。
线程池有哪些
引自博主
newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
基本使用
通过Excetor.的方式就可以选择创建何种线程池,以newCachedThreadPool为例子;
创建对象后,调用execute,传入Runnable对象即可调用其执行方法。
执行结束后,记得关闭线程池,exec.shutdown()
public class CachedThreadPool{
public static void main(String[] args){
ExecutorService exec = Exectors.newCachedThreadPool();
exec.execute(Runnable target);
exec.shutdown();
}
}
关闭线程池
exec.shutdown():不再接受新任务,退出时会等待已经正在执行的任务。exec.shutdownNow: 停止所有正在执行的任务,不再接收新任务,并返回等待执行的任务列表。
那通常情况第一种关闭线程池的方法用的会多一点。