写在最前面:
最近在找国内的工作,投了很多阿里的。面试的时候总在问Java并发编程的问题,备受打击。
在新西兰5年,并没有很多机会接触并发编程。毕业后只是零零碎碎看过一些。由此决定系统的重新学习一下。
原文: https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
什么是并发
当今计算机系统可以在同一时间处理一个或者多个任务。比如视频网站,一个任务在stream data,一个任务去process data。Java在语言层面和library层面都做出了对并发编程的支持。Java的java.util.concurrent是在JDK 5.0以后引入的。
Processes (进程) vs Thread (线程)
进程和线程是并发编程的两个基础单位。在Java并发编程中,我们更关心Thread。但需要注意,进程同样重要。
一个系统里通常有多个活跃的进程和线程。如果系统中只有一个execution core,那么被执行的线程也只会有一个。这种情况下,通过OS的一个叫做time slicing的功能使single core的处理时间会被不同的进程分享。
Processes
一个进程有自己的运行环境。进程的运行时资源是独占的,更进一步说,每个进程都有自己的memory空间。
进程间通过OS提供的Inter Process Communication(IPC)来通信。IPC不仅可以支持同一个OS的不同进程通信,也支持不同OS里的两个进程间通信。
大多数Java都只有一个进程。如果需要创建新的进程需要使用ProcessBuilder类。
Threads
在Java中,通常可以把线程看作一个轻量级的进程。创建线程所要消耗掉的资源少于进程。与进程类似,每个线程都有自己的运行环境。
每一个进程里至少又一个线程。每个线程都会分享所在进程的资源,比如内存。因此,线程之间的通信是十分有效却又容易产生问题的过程。
从一个Java app开发者角度来看,每一个Java app启动的时候都会有一个主线程(main thread)。这个主线程可以用来创建新的线程。
Thread Object
在Java中,我们通常使用两种创建和管理线程的方法。一个是通过Thread在需要的时候创建线程异步的处理任务。另一种方法则是通过Executor。
Runnable & Thread Object
Java提供了两种创建Thread Object并提供Thread需要运行的代码的方法。一种是通过Runnable接口。一种是继承Thread类。以下代码是两种方法的简单实现。
不论哪一种方法,在运行时都是通过调用Thread.start来创建并执行线程。开发者需要在Thread.run或Runnable.run中提供线程需要的代码。这也是Runnable的存在的意义。在JDK8以后,Runnable继承了Functional接口,因此可以用更简洁的Lambda表达方式来创建一个线程。
第一种方法是被广泛提倡的方法。因为他使用接口,所以允许这个类继承更多不同的类从而更灵活的。
Thread.sleep
通过调用Thread.sleep来暂停当前线程一段时间。下面是一个简单的实现
Thread.interrupt & Thread.interrupted
Thread.interrupt是用来干扰当前线程停止目前的任务,做别的任务。比如说,当当前线程正在执行Thread.sleep时,Thread.interuppt会停止线程休眠状态并继续执行下一个任务。以下代码是一个简单的实现
Thread.interrupt会在当前的线程isInterrupted编程true,当下一条指令开始执行时,会被设为false。因此,只有在catch (InterruptedException e)
中重新暂停当前线程才会使得Thread.interrupted返回true。
Thread.join
在多线程的程序中,我们往往需要使优先级高的线程现运行。这就是Thread.join的作用。我们也可以设置join的时间限制。下面是一个简单的例子
如果没有使用Thread.join则打印数字顺序是不定的。当我们使用join的时候,数字的顺序是一定的。