并发编程里的一些概念(一)

前言

前阵子,研究了一下分布式唯一ID的生成方法,接触了多线程的一些东西,发现自己对并发编程的一些基础概念还是一无所知,对于并发的控制还是停留在synchronized的层面,简直不能再低级了,小编可是要成为架构师的男人,怎么可以如此堕落。于是可劲各种谷歌了。今天做一点基础的入门笔记,简单介绍一下多线程编程里的一些概念。

线程的实现

从最基本的开始,我们知道线程最基本的实现有两种。实现Runable接口和继承Thread重写run方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyThread1 extends Thread {

public void run() {
System.out.println("this is myThread1");
}
}
public class MyThread2 implements Runnable{
public void run() {
System.out.println("this is myThread2");
}
}

@Test
public void threadTest() {
Thread thread1 = new MyThread1();
Thread thread2 = new Thread(new MyThread2());
thread1.start();
thread2.start();
}

在JDK1.5后我们有第三种方法实现,那就是实现Callable接口,Callable和Runable区别主要是Callable的call方法有返回值同时能抛出异常,而Runable的run方法没有。同时运行Callable任务能够拿到一个Future对象,通过Future对象我们可以检查任务是否完成,检索任务执行的结果,同时可以取消任务的执行,弥补了Thread的不足。
Callable无法通过new Thread(Runable r)来实现。通常我们可以通过FutureTask来实现,FutureTask实现了Runnable和Future,使得我们依旧能通过Thread启动线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyThread3 implements Callable<Boolean> {
public Boolean call() throws Exception {
System.out.println("this is myThread3");
return true;
}
}
@Test
public void threadTest() {
Thread thread1 = new MyThread1();
Thread thread2 = new Thread(new MyThread2());
FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new MyThread3());
Thread thread3 = new Thread(futureTask);
thread1.start();
thread2.start();
thread3.start();
}

当然我们也可以通过ExecutorService来实现,ExecutorService是线程池里一个概念。我们接着看看线程池的实现。

线程池的实现

在java 5之后,java.util.concurrent里提供了现成的线程池的实现。这个包里的线程的类图:
Executor
看看相关的源码:

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 interface Executor {
void execute(Runnable command);
}

public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();

boolean isShutdown();
boolean isTerminated();

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
...
}

public class Executors {
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
...
}

可以看到ExecutorService通过继承Executor定义了一个线程池,默认线程池实现是ThreadPoolExecutor。我们可以通过Executors创建线程池。
Executors里提供了四种线程池的实现。

1
2
3
4
5
6
7
8
9
//创建一个固定大小的线程池,每次任务从线程池中获取一个空闲线程来执行任务,若无空闲线程则创建新的线程直到线程数达到线程池的最大大小。一旦线程数达到最大数,保持大小不变,在有任务进来则等待线程空闲
newFixedThreadPool(int nThreads);
//创建一个单线程的线程池,即只有一个线程的线程池,所有的任务都串行执行。想不出这个线程的作用,顺序执行意味着可以使任务的执行顺序按照任务提交顺序执行。
newSingleThreadExecutor();
//创建一个可根据需要创建新线程的线程池,没有固定的大小,但是可以像newFixedThreadPool一样创建新的线程。
//其实是有线程数的限制(最大线程数为Integer.MAX_VALUE,在初始化的时候默认设定的),它会将那些已有超过设定的超时时间未被使用的线程从池中移除,估计在高并发情况下使用这个可能会有内存溢出的异常
newCachedThreadPool();
//创建一个大小无限的线程池(这个估计和newCachedThreadPool一样也设定了默认最大线程数),此线程池支持定时以及周期性执行任务的需求。
newScheduledThreadPool();

坚持原创技术分享,您的支持将鼓励我继续创作!