java 中创建线程的方法主要有四种方式,下面我简述这四种方法。
package part11;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
匿名内部类创建对象,重写run()方法,然后通过调用start()方法启动线程,这种方法简单粗暴,但一次只能创建一条线程,线程异常结束,有风险不安全。
package part11;
public class Demo {
static class Task extends Thread{
private int begin;
private int end;
public Task(int begin, int end) {
this.begin = Math.min(begin,end);
this.end = Math.max(begin,end);
}
@Override
public void run() {
for (int i = begin; i <= end; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Task(10,100).start();
}
}
自定义类继承Thread,重写run()方法,然后通过调用start()方法启动线程,这种方法简单粗暴,但一次只能创建一条线程,线程异常结束,有风险不安全。并且这种方法无法解决自定义类已经继承其他类的情况下创建线程的情况。
package part11;
public class Demo {
static class Task2 implements Runnable{
private int begin;
private int end;
public Task2(int begin, int end) {
this.begin = Math.min(begin,end);
this.end = Math.max(begin,end);
}
@Override
public void run() {
for (int i = begin; i <= end; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Task2(10,20)).start();
}
}
自定义类实现Runnable接口,重写run()方法,然后通过调用线程Thread(新建一个Thread或其子类的对象)的start()方法启动线程,这种方法简单粗暴,但一次只能创建一条线程,线程异常结束,有风险不安全。并且这种方法可以解决自定义类已经继承其他类的情况下创建线程的情况。
package part11;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
es.submit(new Task2(10,20));
//缓存线程池和固定线程池的结合,3代表核心线程池,只开三条线程池
ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
//第一次延迟5秒,以后每次延迟10秒启动线程
ses.scheduleWithFixedDelay(new Task2(1,5),5,10,TimeUnit.SECONDS);
}
}
1. 缓存线程池:要就给你开,无上限(不超过JVM控制的线程)小型任务
ExecutorService es = Executors.newCachedThreadPool();
CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来,如果线程有可用的,就使用之前创建好的线程,如果没
有可用的,就新创建线程,终止并且从缓存中移除已有60秒未被使用的线程。
2. 固定线程池:并发有上限 中大型任务
ExecutorService es2 = Executors.newFixedThreadPool(10);
在FixedThreadPool中,有一个固定大小的池,如果当前需要执行的任务超过了10,就是超过池的大小,那么多于的任务处于
等待状态,直到有空闲下来的线程执行任务,而当执行的任务小于池大小,空闲的线程也不会去销毁。
3. 单线程池
ExecutorService es3 = Executors.newSingleThreadExecutor();
一个单个的线程,这个线程会保证你的任务执行完成,如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直
接创建线程不同
4. 定时任务线程池
ExecutorService es4 = Executors.newScheduledThreadPool(10);
常用:ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
缓存线程池和固定线程池的结合,3代表核心线程池,只开三条线程池
ses.scheduleWithFixedDelay(new Task2(1,5),5,10,TimeUnit.SECONDS);
第一次延迟5秒,以后每次延迟10秒启动线程。
此外,多线程还可以传入实现Callable接口的实现类对象。Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。