一直以来,对于java的多线程变成都不是十分了解,趁着项目需要一个多线程的爬虫学习了一下,主要是一些基础的相关知识,以上。
Runnable和Thread
踩的第一个坑是关于实现Runnable接口和继承Thread类来build一个新的可运行线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class MyRunnable implements Runnable { private int count = 5; @Override public void run() { System.out.println(this.count--); } }
public class MyThread extends Thread { private int count = 5; @Override public void run() { System.out.println(this.count--); } }
|
1 2 3 4 5 6 7
| public static void main(String[] args) { while (int i = 0; i < 5; i++) { new MyRunnable().run(); } }
|
在main中分别启动两个类的5个线程,可以看到Runnable接口并没有实际上的并发的启动几个线程,而是在第一个线程执行完毕之后,再去执行第二个线程,阻塞式地执行完new出来的5个线程,究其原因,是因为Runnable接口实际上并不是实际的Thread的入口
,Runnable接口只是定义了Thread的任务逻辑
,也就是说Runnable接口中的run()方法只是实现了现在需要的业务逻辑,实际的多线程仍然需要通过Thread类开始。
多线程的执行
在实际的线程执行的时候,较为传统的做法是通过new Thread(new Runnable()).start()
来执行,然而新建线程实际上也是一个较为繁重的操作,以下是反编译的Thread类代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public synchronized void start() { if (this.threadStatus != 0) { throw new IllegalThreadStateException(); } else { this.group.add(this); boolean started = false;
try { this.start0(); started = true; } finally { try { if (!started) { this.group.threadStartFailed(this); } } catch (Throwable var8) { }
}
} }
private native void start0();
|
以上可以看出,Thread类的start()方法,最终调用了native的start0(),最终通过JNI(java native interface)调用底层提供的pthread_create方法,最终进入linux系统提供的创建线程的接口。
在javase 1.5提供了一个Concurrent的包来提供方便运行的多线程类库,如提供运行的ExecutorService,提供线程安全的各类Blocking Queue、Blocking List、Concurrent List等,以及一些原子操作类库。
其主要通过ExecutorService来启动需要执行的线程
1 2 3 4 5 6 7 8 9 10 11 12 13
| ExecutorService e1 = Executors.newCachedThreadPool();
ExecutorService e1 = Executors.newCachedThreadPool(int poolSize);
ExecutorService e3 = Executors.newScheduledThreadPool(int poolSize);
ExecutorService e4 = Executors.newSingleThreadExecutor();
e1.submit(() -> { })
|
任务通过ExecutorService提交到指定的线程池,或者执行线程中异步
的执行,即submit函数不会阻塞主线程的执行,最终子任务的逻辑会异步的在新线程中执行
但是如果主线程依赖子线程的运行结果,在submit方法执行后,返回了一个Future对象,Future对象可以在主线程中控制子线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ExecutorService ex = Executors.newCachedThreadPool(); Future f = ex.submit(() -> { try { Thread.sleep(10000) } catch (Exception e) { e.printStackTrace(); } })
f.get()
try { f.get(1000, TimeUnit.MilliSeconds) } catch(TimeoutException | InterruptedException | ExecutionException e) { e.printStackTrace(); }
|