1.1 线程
1.1.1进程和线程
进程
进程:进程指正在运行的程序,进程拥有一个完整的、私有的基本运行资源集合。通常,每个进程都有自己的内存空间。
进程往往被看作是程序或应用的代名词,然而,用户看到的一个单独的应用程序实际上可能是一组相互协作的进程集合。
为了便于进程之间的通信,大多数操作系统都支持进程间通信(IPC),如pipes 和sockets。IPC不仅支持同一系统上的通信,也支持不同的系统。IPC通信方式包括管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等方式,其中 Socket和Streams支持不同主机上的两个进程IPC。
线程
线程有时也被称为轻量级的进程。进程和线程都提供了一个执行环境,但创建一个新的线程比创建一个新的进程需要的资源要少。
线程是在进程中存在的 — 每个进程最少有一个线程。线程共享进程的资源,包括内存和打开的文件。这样提高了效率,但潜在的问题就是线程间的通信。
多线程的执行是Java平台的一个基本特征。每个应用都至少有一个线程 – 或几个,如果算上“系统”线程的话,比如内存管理和信号处理等。但是从程序员的角度来看,启动的只有一个线程,叫主线程。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
1.1.2 线程实践
1.1.2.1 线程的创建
两种方式:
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
方式2:
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
1.1.2.2 线程启动和停止
启动线程
调用start方法
停止线程
线程自带的stop方法,一方面已经过时,另一方面,不会对停止的线程做状态保存,使得线程中涉及的对象处于未知状态,如果这些状态,其他线程也会使用,将会使得其他线程出现无法预料的异常,所以,停止程序的功能,需要自己实现。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(1000L);
thread.stop();
while (thread.isAlive()) {
thread.print();
}
}
private static class StopThread extends Thread {
private int x = 0; private int y = 0;
@Override
public void run() {
synchronized (this) {
++x;
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
++y;
}
}
public void print() {
System.out.println("x=" + x + " y=" + y);
}
}
}
上述代码中,run方法里是一个同步的原子操作,x和y必须要共同增加,然而这里如果调用thread.stop()方法强制中断线程,输出如下:
x=1 y=0
没有异常,也破坏了我们的预期。如果这种问题出现在我们的程序中,会引发难以预期的异常。因此这种不安全的方式很早就被废弃了。
public class MyRunnable implements Runnable {
private boolean doStop = false;
public synchronized void doStop() {
this.doStop = true;
}
private synchronized boolean keepRunning() {
return this.doStop == false;
}
@Override
public void run() {
while(keepRunning()) {
// keep doing what this thread should do.
System.out.println("Running");
try {
Thread.sleep(3L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
调用
public class MyRunnableMain {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
try {
Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
myRunnable.doStop();
}
}
1.1.2.3 线程暂停和中断
暂停
Java中线程的暂停是调用java.lang.Thread
类的sleep
方法。该方法会使当前正在执行的线程暂停指定的一段时间,如果线程持有锁,sleep
方法结束前并不会释放该锁。
中断
java.lang.Thread类有一个interrupt
方法,该方法直接对线程调用。当被interrupt的线程正在sleep或wait时,会抛出InterruptedException
异常。
事实上,interrupt
方法只是改变目标线程的中断状态(interrupt status),而那些会抛出InterruptedException
异常的方法,如wait、sleep、join等,都是在方法内部不断地检查中断状态的值。
- interrupt方法
Thread实例方法:必须由其它线程获取被调用线程的实例后,进行调用。实际上,只是改变了被调用线程的内部中断状态;
- Thread.interrupted方法
Thread类方法:必须在当前执行线程内调用,该方法返回当前线程的内部中断状态,然后清除中断状态(置为false) ;
- isInterrupted方法
Thread实例方法:用来检查指定线程的中断状态。当线程为中断状态时,会返回true;否则返回false。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(1000L);
thread.interrupt();
while (thread.isAlive()) {
thread.print();
}
}
private static class StopThread extends Thread {
private int x = 0; private int y = 0;
@Override
public void run() {
synchronized (this) {
++x;
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
++y;
}
}
public void print() {
System.out.println("x=" + x + " y=" + y);
}
}
}
输出结果如下:
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0
x=1 y=0 有时候x=1 y=1
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at ThreadTest$StopThread.run(ThreadTest.java:28)
java.lang.InterruptedException: sleep interrupted 这个结果是符合我们的预期,同时还抛出了个异常。
底层源码实现:
// 核心 interrupt 方法
public void interrupt() {
if (this != Thread.currentThread()) // 非本线程,需要检查权限
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 仅仅设置interrupt标志位
b.interrupt(this); // 调用如 I/O 操作定义的中断方法
return;
}
}
interrupt0();
}
// 静态方法,这个方法有点坑,调用该方法调用后会清除中断状态。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 这个方法不会清除中断状态
public boolean isInterrupted() {
return isInterrupted(false);
}
// 上面两个方法会调用这个本地方法,参数代表是否清除中断状态
private native boolean isInterrupted(boolean ClearInterrupted);
interrupt() :
- interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
- 如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;
- 如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
- 如果线程在Selector上被阻塞,select方法将立即返回;
- 如果非以上情况,将直接标记 interrupt 状态;
注意:interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的线程,不会受 interrupt 中断;
阻塞情况下中断,抛出异常后线程恢复非中断状态,即 interrupted = false
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Task("mytask"));
t.start();
t.interrupt();
}
static class Task implements Runnable{
String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("thread has been interrupt!");
}
System.out.println("isInterrupted: " + Thread.currentThread().isInterrupted());
System.out.println("task " + name + " is over");
}
}
}
输出:
thread has been interrupt!
isInterrupted:false
task mytask is over
调用Thread.interrupted() 方法后线程恢复非中断状态
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Task("mytask"));
t.start();
t.interrupt();
}
static class Task implements Runnable{
String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("first :" + Thread.interrupted());
System.out.println("second:" + Thread.interrupted());
System.out.println("task " + name + " is over");
}
}
}
输出结果:
first:true
second:false
task mytask is over
1.1.2.4 线程的状态
Java线程可能的状态:
线程的状态变迁
1.2 多线程
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
1.2.1 并发和并行
- 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
- 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
- 在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群
1.2.2 多线程好处
提高cpu的利用率
单线程:
5 seconds reading file A
2 seconds processing file A
5 seconds reading file B
2 seconds processing file B
-----------------------
14 seconds total
多线程
5 seconds reading file A
5 seconds reading file B + 2 seconds processing file A
2 seconds processing file B
-----------------------
12 seconds total
一般来说,在等待磁盘IO,网络IO或者等待用户输入时,CPU可以同时去处理其他任务。
更高效的响应
多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态,不会造成无法响应的现象。
公平使用CPU资源
当前没有进行处理的任务,可以将处理器时间让给其它任务;占用大量处理时间的任务,也可以定期将处理器时间让给其它任务;通过对CPU时间的划分,使得CPU时间片可以在多个线程之间切换,避免需要长时间处理的线程独占CPU,导致其它线程长时间等待。
1.2.3 多线程的代价
更复杂的设计
共享数据的读取,数据的安全性,线程之间的交互,线程的同步等;
上下文环境切换
线程切换,cpu需要保存本地数据、程序指针等内容;
更多的资源消耗
每个线程都需要内存维护自己的本地栈信息,操作系统也需要资源对线程进行管理维护;