<commit_before>---
layout: post
title: "深入理解java虚拟机 —— java 线程"
subtitle: ""
date: 2017-05-09
author: "ChenJY"
header-img: "img/drive.jpg"
catalog: true
tags: 
    - 深入理解Java虚拟机
---

Java里面谈论并发，一般都与线程脱不了干系。

### 线程的实现



### 参考资料
* 《深入理解Java虚拟机》 周志明著<commit_msg>20170713<commit_after>---
layout: post
title: "深入理解java虚拟机 —— 线程"
subtitle: ""
date: 2017-05-09
author: "ChenJY"
header-img: "img/drive.jpg"
catalog: true
tags: 
    - 深入理解Java虚拟机
---

Java里面谈论并发，一般都与线程脱不了干系。

### 进程和线程的区别
进程是cpu资源分配的最小单位，线程是cpu调度的最小单位。以前进程既是资源分配也是调度的最小单位，后来为了更合理的使用cpu(实际上是cpu性能越来越好)，才将资源分配和调度分开，就有了线程。线程是建立在进程的基础上的一次程序运行单位。

线程是比进程更轻量级的调度执行单位，线程的引入可以把一个进程的资源分配和执行调度分开，各个线程既可以共享进程资源，又可以独立调度。java中Thread类与大部分的java api有显著的区别，他的所有关键方法都是声明为Native的，一个Native方法往往意味着这个方法没有使用或无法使用平台无关的手段来实现。一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法：该方法的实现由非java语言实现，比如C。

### 线程的实现
实现线程主要有三种方式：使用内核线程实现，使用用户线程实现和使用用户线程加轻量级进程混合实现

#### 使用内核线程实现
内核线程就是直接由操作系统内核支持的线程，这种线程由内核完成线程切换，内核通过操纵调度器对线程进行调度，并负责将线程的任务映射到各个处理器上。程序一般不会直接去使用内核线程，而是去使用内核线程的一种高级接口——轻量级进程LWP，由于内核线程的支持，每个轻量级进程都会成为一个独立的调度单元，即使一个轻量级进程在系统调用中阻塞了，也不会影响整个进程继续工作，这种进程与用户线程之间是1:1的关系，但是LWP也有局限性：
* 由于是基于内核线程实现的，所以各种线程操作，如创建、析构以及同步都需要进行系统调用，代价较高，需要在用户态和内核态中来回切换
* 每个轻量级进程都需要一个内核线程的支持，因此轻量级进程要消耗一定的内核资源，因此一个系统支持的轻量级进程是有限的。

#### 使用用户线程实现
广义上，只要一个线程不是内核线程就可以认为是用户线程，因此LWP也属于用户进程。狭义上用户线程指的是完全建立在用户空间的线程库上，系统内核也不能感知线程存在的实现，用户线程的建立、同步销毁和调度完全在用户态中完成，不需要内核的帮助，如果实现得当这种线程不需要切换到内核态，因此操作可以是非常快速且低消耗的，也可以支持更大的线程数量，部分高性能数据库中的多线程就是由用户线程实现的。这种进程与用户线程之间是1:N的关系。其优势在于不需要系统内核的支援，缺点是所有线程操作都需要用户程序自己处理，诸如“阻塞处理”、“多处理器系统如何将线程映射到其他处理器上”这类问题解决起来很麻烦，因而使用用户线程实现的程序一般都比较复杂。

#### 使用用户线程加轻量级进程混合实现
用户线程还是完全建立在用户态中，可支持大规模用户线程并发；而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁，这样可以使用内核提供的线程调度功能以及处理器映射，并且用户线程的系统调用要通过轻量级线程来完成，大大降低了整个进程被完全阻塞的风险。这种模式下用户线程与轻量级进程的数量比是不确定的，即为N:M的关系。

#### Java 线程的实现
对于Sun JDK来说，它的windows版与Linux版都是使用一对一的线程模型实现的。

### Java 线程调度
线程调度是指系统为线程分配处理器使用权的过程，主要调度方式有两种，分别是协同式线程调度和抢占式线程调度。协同式中线程的执行时间由线程本身控制，当线程把自己的工作执行完了之后，主动通知系统切换到另一个线程上，好处是简单，切换操作对于线程自己是可知的，没什么线程同步问题。坏处是线程执行时间不可控，可能会一直阻塞然后系统崩溃。

抢占式的话，每个线程由系统分配执行时间，不由线程本身决定。线程的执行时间是系统可控的，不会有一直阻塞的问题，Java使用抢占式调度。Java中可以通过设置优先级来达到给一些线程多一点时间，给一些线程少一点时间的目的。Java语言一共有10个级别的线程优先级，优先级高的越容易被系统选择执行。但是Java的线程是通过映射到系统原生的线程上来实现的，所以线程调度最终还是取决于系统。

### 状态转换
java语言定义了5中线程状态，任何一个时间点，一个线程只能有其中一个状态，分别是：
* 新建：创建后尚未启动
* 运行：包括了操作系统线程状态中的Running和Ready
* 无限期等待：不会被分配CPU执行时间，等待被其他线程显式地唤醒
* 限期等待：不会被分配CPU执行时间，但是无需被其他线程显式唤醒，而是一定时间后由系统唤醒
* 阻塞：阻塞状态等待获取一个排它锁，这个事件将在另一个线程放弃这个锁的时候发生；而等待状态是等待一段时间或者唤醒动作的发生。
* 结束：已终止的线程 

![image](http://dl.iteye.com/upload/picture/pic/116719/7e76cc17-0ad5-3ff3-954e-1f83463519d1.jpg)

### 参考资料
* 《深入理解Java虚拟机》 周志明著
{'fraction_non_alphanumeric': 0.1002710027100271, 'fraction_numerical': 0.02245451025938831, 'mean_word_length': 14.023255813953488, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3287977', 'n_tokens_mistral': 2229, 'n_tokens_neox': 2631, 'n_words': 78}