1、什么是进程、线程、多线程 ?
操作系统中,进程是分配和调度系统资源的最小单位,而线程是进程中的能独立执行的最小单元 ;
一个进程中至少有一个线程,称为主线程,主线程还能创建其他线程,所有线程称为多线程 ;
在单核CPU操作系统中,多个线程或者进程采用时间片转轮的抢占式方式或取CPU,由于单个时间片很短,给人的感觉就像是多个进程或线程在同时执行,这是伪并行;
在多核CPU操作系统中,线程或进程数小于CPU数量,那么足以每个进程或者线程各分配一个CPU同时运行,实现真正的并行 。
2、 JVM 线程与操作系统的线程啥关系 ?
先理解 线程模型
- 内核级别线程
内核级别线程(Kernel-level threading)是指用户进程通过内核提供API创建的线程。这样用户进程里的线程和内核调度的线程是1:1对应的。这也就最简单的线程管理方法。
- 用户级别线程
用户基本线程(User-level threading)是指 用户进程自己实现线程机制。在这种情况下,内核是不知道有多少线程存在的,同时线程上下文切换更快,而且在不支持多线程的操作系统上也能实现多线程。但是,用户级别的线程也带来相当大的坏处,比如不能充分利用多处理器(多核)系统;如果进程中的一个线程执行阻塞的I/O请求,那么整个用户进程都会被阻塞。
- 混合线程
混合线程是内核级别线程和用户级别线程的混合体,提供了以上两种模型的优点,但是带来很大的复杂性。
JVM 1.1之前使用的是 用户级别线程,又称绿色线程 。
JVM 1.1之后,由于用户级别线程的局限性,之后改为映射内核级别线程 或者进程 。
在linux2.6之前,操作系统还没有线程,当时映射的是进程和微进程,微进程能进行一定的互相通信,进程之间是完全不能互相通信的 。
linux 2.6 之后,就是目前的,就都是内核级别线程了。
3、java 线程的创建(2种方式)
阅读java.lang.Thread类的源码:
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
很明显,如果我们要创建一个能跑自己逻辑的Thread,就只有2个途径
1、为属性target赋值一个实现了Runnable对象,该对象实现了自己的run方法 。而这个赋值没有set方法,只有一个途径,构造函数(源码如下)
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
2、类继承Thread,并且重写run方法 。
示例如下 :
/**
* 线程基础一:线程的创建
* @author xubo
*
*/
public class ThreadStudyOne {
/**
* 通过Thread(Runnable A)进行创建
*/
private static Thread getThreadFromRunnable() {
return new Thread(new Runnable() {
@Override
public void run() {
//what you want to do !
System.out.println("这是通过Thread(Runnable A)构造函数创建的");
}
});
}
/**
* 继承
*/
public static class myThread extends Thread {
@Override
public void run() {
//what you want to do !
System.out.println("这是通过重写父类Thread的run方法创建的");
}
}
private static Thread getThreadFromOverride() {
return new myThread();
}
}
4、进程的调度 ,必须使用Thread.start()方法,直接使用run方法是没有创建线程的 。
源码如下
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
5、Thread常用方法 与 属性
a、先级 priority :
有get与set方法 ,有低中高3个常量,Thread.MIN_PRIORITY=0,Thread.NORM_PRIORITY=5,THREAD.MAX_PRIORITY=10
b、执行 start() :
调用操作系统内核API创建一个线程进行执行任务 。
c、停止 stop() :
调用瞬间,对应线程抛出ThreadDeathException,导致线程异常终止,会有监视器(锁对象)未释放的风险,
目前在JDK中标记为过时
d、打断 interrupt() :
调用interrupt()方法,对应线程如果处于sleep睡眠状态,会直接跑出Interrupted异常,否则对应线程
继续执行,只是状态改为了打断状态 。这个方法只能终止睡眠状态线程,使用时根据需要考虑stop()与interrup()。
e、睡眠 sleep ():
sleep(Long m) 表示睡眠多少毫秒 。 如果是sleep(0),表示希望系统直接进行一次CPT资源抢占分配 ,
给低优先级线程一个运行的机会。该方法不会释放锁。
f、暂停 与 唤醒 wait() ,notify(),notifyAll(),yield() :
wait(Long m) : 线程阻塞并释放锁,直到被notify()或notifyAll唤醒 ,或者等待m毫秒时间。
wait() : 线程阻塞并释放锁,知道被notify()或notifyAll唤醒,否则一直等待
yield() : 线程阻塞并释放锁,知道线程组里面的其他线程执行完成再执行本线程。
g、线程组 ThreadGroup :
对线程进行了隔离 。 只有同一线程组内的线程才能够互相调用与阻塞。不自己设置线程组的情况下,
都是继承创建该线程的线程组。以此向上推,可知,同一个线程源头创建的线程一般都属于一个线程组。
是否有权限操作某线程a,可用a.checkAccess()方法。
h、执行等待 join() :
join(Thread A) , 强制在A线程执行完后再继续执行本线程。
i、守护线程
与基本线程其实也没是区别,
主要特性2点:作用是为被守护的线程提供监控和服务;会因为所有被守护的线程的结束而自动结束。
调用setDaemon()方法就能设置守护线程了,在守护线程中创建的线程都是守护线程。