创建线程的几种方式
直接创建线程
Java 中实现多线程有两种方法:继承 Thread 类、实现 Runnable 接口。在程序开发中只要是多线程,肯定永远以实现 Runnable 接口为主,因为实现 Runnable 接口相比继承 Thread 类有如下优势:
可以避免由于 Java 的单继承特性而带来的局限; 增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的; 适合多个相同程序代码的线程区处理同一资源的情况。
多线程的实现方法:http://wiki.jikexueyuan.com/project/java-concurrency/function.html 如何创建并运行 java 线程: http://wiki.jikexueyuan.com/project/java-concurrent/creating-and-starting-java-threads.html
1.继承 Thread 类
public class Task extends Thread {
@Override
public void run() {
}
}
public class Test {
public static void main(String[] args) {
Thread thread = new Task();
thread.start();
}
}
写成 Thread thread = new Thread(new Task());
也可以,因为 Thread 也继承了 Runnable 接口, public class Thread implements Runnable {}
。
- 实现 Runnable 接口。
需要重写 run 方法 public abstract void run();
。
调用 start()
方法会进行一些初始化操作,然后会调用 run()
执行。
public class Task implements Runnable {
@Override
public void run() {
}
}
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.start();
}
}
可以直接调用任务,但是这样是直接占用调用该 run 方法的 线程。
public static void main(String[] args) {
Task task = new Task();
task.run();
}
常见错误:创建并运行一个线程所犯的常见错误是调用线程的 run()方法而非 start()方法。 事实上,run()方法并非是由刚创建的新线程所执行的,而是被创建新线程的当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行 run()方法,必须调用新线程的 start 方法。
Thread newThread = new Thread(MyRunnable());
newThread.run(); //should be start();
另外,创建线程时的简写方式:
(new Thread(){
@Override
public void run(){
System.out.println("Thread Running");
}
}).start();
这种其实是创建了一个Thread的匿名子类。
用lambda 简化如下:
(new Thread(() -> System.out.println("Thread Running"))).start();
public class T {
public static void main(String[] args) {
T t = new T();
new Thread(t::m, "t1").start();
new Thread(() -> {
t.m();
}, "t2").start();
}
void m() {
System.out.println("hello");
}
}
Executor 框架 & 线程池
http://wiki.jikexueyuan.com/project/java-concurrent/thread-pools.html
http://wiki.jikexueyuan.com/project/java-concurrency/executor.html
java5 新功能。 Executor 框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable 等。
Executor 执行 Runnable 任务
Executor 接口中之定义了一个方法 execute(Runnable command),来执行一个任务。
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
// ExecutorService executorService = Executors.newScheduledThreadPool(5);
// ExecutorService executorService = Executors.newFixedThreadPool(5);
// ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.execute(new TestRunnable());
System.out.println("************* a" + i + " *************");
}
executorService.shutdown();
}
}
class TestRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程被调用了。");
}
}
源代码
public interface Executor {
void execute(Runnable command);
}
public interface ExecutorService extends Executor {
...
}
Executor 执行 Callable 任务
实现了 Callable 接口的类的任务有返回值。通过 ExecutorService 的 submit(Callable task) 方法来执行。
可以看到 Callable
接口中只有一个 call
方法,需要重写。
public interface Callable<V> {
V call() throws Exception;
}
public class CallableDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = executorService.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
resultList.add(future);
}
//遍历任务的结果
for (Future<String> fs : resultList) {
try {
//Future返回如果没有完成,则一直循环等待,直到Future返回完成
while (!fs.isDone()) { }
//打印各个线程(任务)执行的结果
System.out.println(fs.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
}
}
}
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
/**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,
* 则该方法自动在一个线程上执行
*/
@Override
public String call() throws Exception {
System.out.println("call()方法被自动调用! " + Thread.currentThread().getName());
Thread.sleep(10000);
//该返回结果将被Future的get方法得到
return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
}
}
submit
之后,call
方法就会执行,并在执行完之后将结果返回,通过 get()
方法获取。isDone()
方法有没有必要?因为即使不用 isDone 检查,get 方法也会一直阻塞知道获取结果的。
需要注意,看以下源代码可知,submit方法其实也可以传入 Runnable 对象,只不过返回值是 null,或者可以一开始传入一个值用于返回。
public abstract class AbstractExecutorService implements ExecutorService {
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
}
newCachedThreadPool 会先检查是否有空闲的线程,有的话重用,没有则创建新的。
注意几种创建线程池的方法的区别。
自定义线程池
public class ThreadPoolTest {
public static void main(String[] args) {
//创建等待队列
BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<>(20);
//创建线程池,池中保存的线程数为3,允许的最大线程数为5
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 50, TimeUnit.MILLISECONDS, bqueue);
//创建七个任务
Runnable t1 = new MyThread();
Runnable t2 = new MyThread();
Runnable t3 = new MyThread();
Runnable t4 = new MyThread();
Runnable t5 = new MyThread();
Runnable t6 = new MyThread();
Runnable t7 = new MyThread();
//每个任务会在一个线程上执行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
pool.execute(t7);
//关闭线程池
pool.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在执行。。。");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
参考
- 《think in java》 21章