线程和进程
进程就是计算机中正在进行的一个独立的应用程序,进程是一个动态的概念必须是进行状态,如果一个应用没有启动,那就不是进程
线程就是组成进程的基本单元,可以完成特定的功能,一个进程可以有多个线程组成
进程和线程的区别:
1、内存空间上的区别:
进程是有独有的内存空间,每个进程之间是相互独立的,互不影响
线程有共享的内存空间(也有私有空间)
2、安全性
进程是相互独立的,一个进程的奔溃不会影响到其他的进程,进程是安全
线程存在内存空间共享,一个线程的奔溃可能会影响到其他的线程的执行,线程的安全是不如进程
- 关系:进程是相互独立的,一个进程下可以有一个或者多个线程
Java中是很少使用进程概念,但也可以使用
//Java中创建进程方式
Runtime runtime = Runtime.getRuntime();
Java启动线程
private native void start0();
native方法会调用底层操作系统支持,Java本身是没办法启动线程的,线程启动是需要依赖底层操作系统支持的,Java调用native方法,调用底层操作系统C++编写的动态函数库,有C++去操作底层启动线程。Java是间接的调用来启动线程
线程创建方式
● 继承thread类
● 实现Runable接口
● 实现Callable接口
实现Runable接口
Runable接口定义如下:
public interface Runnable {
//抽象的run方法
public abstract void run();
}
Runable接口中提供了一个抽象的run方法,实现RUnable接口实现run方法
Runnable实现方式
/**
* 通过实现Runnable接口来进行run方法
*/
public class RunableDemo implements Runnable {
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName()+"的线程在执行");
}
}
RunableDemo runableDemo = new RunableDemo();
Thread thread = new Thread(runableDemo);
//当前才会创建子线程
thread.start();
Runnable实现类是任务,Thread类是线程对象,线程是用来执行任务的
实现Runnable的线程创建的步骤:
- 创建一个实现Runnable接口的实现类,并实现run方法
- 实例化Runnable接口实现类
- 创建Thread类实例,将实例化Runnable接口实现类作为参数传递
- 启动子线程,调用Thread类的实例的start
继承Thread类
Thread类的定义
- public class Thread implements Runnable
可以看出:Thread类实现了Runnable接口,即Thread类也是Runnable接口实现类,在Thread中业余run方法的实现`
public void run() {
if (target != null) {
target.run();
}
}
Thread类中的run方法的实现中,target任务体不能为空,即调用run方法,需要通过Thread类来重写run方法
继承Thread类使用Demo
/**
* 通过继承Thread类来重写run方法
*/
public class ThreadDemo extends Thread {
@Override
public void run() {
System.out.println("ThreadDemo类 线程名:"+
Thread.currentThread().getName()+" 在执行");
}
}
//第二种:通过继承Thread类来创建线程
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
继承Thread类创建线程步骤:
- 创建类,继承Thread重写run方法
- 实例化当前创建的额Thread类的子类
- 启动子线程、调用start方法
实现Callable接口
Callable接口声明如下:
public interface Callable<V> {
V call() throws Exception;
}
Callable接口提供了call方法,具有返回值,通过泛型来定义,该接口可以抛出异常
该接口的实现类是无法直接使用,需要借助于FutureTask类
public class FutureTask<V> implements RunnableFuture<V> {
//该类能接口Callable实现类
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
RunnableFuture接口声明
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
FutureTask类是继承自Runnable接口,即FutureTask是Runable接口的实现类,而FutureTask类提供构造函数FutureTask(Callable callable),可以接受Callable类型的任务
就可以借助FutureTask将Callable类型的实例转化为Runnable类型的实例
Callable接口实现的Demo
public class CallableDemo implements Callable {
@Override
public Object call() throws Exception {
System.out.println("CallableDemo类 线程名:"+Thread.currentThread().getName()+"在执行");
return null;
}
}
//实例化Callable接口实现类
CallableDemo callableDemo = new CallableDemo();
//通过FutureTask可以将Callable实例转化为Runable实例
FutureTask <String> futureTask = new FutureTask <String>(callableDemo);
Thread thread1 = new Thread(futureTask);
thread1.start();
实现Callable接口创建线程步骤
- 创建类,实现Callable接口,实现其提供的call方法
- 创建Callable实例
- 创建FutureTask实例,将Callable实例作为参数传入
- 创建Thread实例,将FutureTask实例作为参数传递(当做Runable实例)
- 启动子线程,调用Thread类的Start方法
- 线程的状态及状态转换
线程状态
JDK中提供的线程状态在Thread类中的一个子枚举类
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
- 新建状态(NEW)
使用new语句创建的线程处于新建状态(new Thread),仅仅在堆上分配了内存 - 就绪状态(Runnable)
当线程创建后,其他线程调用start方法,该线程就进入到就绪状态,JVM就会为创建方法调用栈和程序计数器,处于这个状态的线程位于可运行的池中,等待获取CPU的使用权
其他处于则塞状态解除阻塞之后也会进入就绪状态 - 运行状态(Running)
处于这个状态的线程占用CPU,执行程序代码,只有处于就绪状态的额线程才会有机会转到运行状态 - 阻塞状态(Blocked)
当线程由于缺少响应的资源而导致程序无法继续执行,就会从运行状态进入到阻塞状态比 如:锁资源,IO资源 - 等待状态(Waiting)
当线程调用了wait方法就会进入到waiting状态,该状态只有notify等操作才能唤醒线程进入下一个状态 - 睡眠状态(Time_Waiting)
如果线程执行了sleep(long)/join(long)/wait(long),会触发线程进入到Time_waiting状态,只有到达设定的时间,才会脱离阻塞状态 - 终止状态(Terainated)
当线程退出run方法,就进入到终止状态,该线程结束生命周期
线程状态转化
一个线程的生命周期需要状态:New、Runnable、Running、Terminated状态
线程在需要响应资源时,就会进入到阻塞状态,阻塞状态包含Waiting、Blocked、Time_waiting状态
线程方法
start():启动线程
start方法作用是启动一个新线程执行,start需要首先调用,而且不能不能重复调用
start方法会使线程从新建状态进入到就绪状态
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
start方法启动子线程是通过调用系统提供的方式来启动线程
run():子线程的执行体
run方法和普通成员方法一样,可以重复调用
单独调用run方法,会在当前线程中执行run方法,并不会启动新线程
yield():线程让步
yield方法暂停当前线程的执行,并让步于其他同优先级的线程,让其他线程先执行
特点:
- yield方法的让步,能让正在执行的线程由“运行状态”进入到“就绪状态”,等待调度执行
- yield仅仅是让出CPU资源,让给谁,是有系统决定的,系统会让其他相同优先级或者是更高优先级的线程来获取执行权,要是没有更高优先级的线程,会继续执行原来的线程
public static native void yield();
是定义在Thread类中的静态方法
join():线程合并
作用:
暂停当前线程的执行,等待子线程执行,也叫做线程合并,join方法做的事情就是讲并行执行的线程合并为串行执行
例如:如果在a线程中调用b线程的join方法即b.join方法,则会让b线程先执行,直到b线程执行结束,a线程才能继续执行
方法介绍:
join方法是在Thread类中,可以被中断掉
t.join()是允许t线程插队到当前线程前面,等t线程执行完成再执行当前线程
t.join(millis)是允许t线程插队到当前线程前面,等t线程执行且最长等待millis毫秒再执行当前线程
t.join(millis,nanos)和t.join(millis)类似,给定了有限时间等待,只是提供了一个纳秒级精度
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
//特殊标识,标识一致阻塞
while (isAlive()) {
wait(0);
}
} else {
//millis大于0,会阻塞到最多millis时间
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public final synchronized void join(long millis, int nanos)throws InterruptedException {
if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}
if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
public final void join() throws InterruptedException {
join(0);
}
方法特点:
- 当前线程a调用b.join(),b线程会开始执行,a线程进入到阻塞状态
- join方法hi被中断,如果有Interrupt方法会导致当前阻塞提前结束,会抛出InterruptedException
sleep():线程休眠
会是当前线程进入休眠
特点:在sleep期间,线程运行状态从运行进入到阻塞状态,会让出CPU的执行权,当线程被重新唤醒时,会由阻塞状态进入到就绪状态,等待CPU的使用权
方法介绍:
sleep方法主要有两个,sleep(long millis) 和sleep(long millis, int nanos)两个方法功能类似,后一个方法提供更高精度的纳秒级控制
sleep方法是Thread里提供的静态方法,当前的方法也是可以抛出InterruptedException,可以被Interrupt中断掉
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos)throws InterruptedException {
if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}
if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
Interrupt():中断线程
Interrupt方法是中断当前线程,终止处于阻塞状态的线程
Interrupt方法在Thread类中,是一个普通方法,可以都对象来调用
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
private native void interrupt0();
interrupt特点
interrupt方法调用仅仅修改了一个中断标志
如果当前线程是可中断的状态(调用了sleep、join、wait等方法导致线程进入阻塞状态)在其他线程中调用interrupt方法,会立即抛出一个InterruptedException,来停止掉中断
如果当前处于运行状态,调用interrupt方法,线程会继续执行,直到发生sleep、join、wait等方法的调用,才会进入阻塞
deamon:守护线程
守护线程存在两个方法
setDaemon(boolean on) 设置守护线程,参数是true和false,true表明设置为守护线程, false,设置为非守护线程 ,默认是false
boolean isDaemon()判断是否守护线程,返回Boolean类型
守护线程:Java线程有两种,一种是守护线程,一种是用户线程
用户线程一般用户执行用户级任务,一般创建的线程都是用户线程
守护线程也叫做“后台线程”服务于用户线程,一般就是用来执行后台任务,例如:垃圾回收是专门线程来处理的,负责垃圾回收的线程就是守护线程
守护线程的生命周期:
守护线程的生命周期依赖于用户线程,当用户线程存在,守护线程就会存在,当用户线程不存在,那么守护线程也就会随之消亡
Priority:线程优先级
线程优先级,就是用来指导线程执行的优先权
方法介绍:
int getPriority():获取线程的优先级setPriority(int newPriority),设置当前线程的优先级,newPriority必须是1-10之间的额整数,否则会抛出异常
优先级范围:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
设置Java线程的优先级,最小值为1,默认值是5,最大值是10
方法特点:
- 线程的优先级并不绝对,所控制的是执行的机会,优先级高的线程执行的概率是比较大,而优先级低的线程也并不是没有机会,只是执行的概率会低一些
- 优先级工10个优先级,分别为1-10,数值越大,表名优先级越高,普通的线程,优先级是5
- 线程的优先级是用来指导JVM层面优先那个线程执行,最终执行顺序需要操作系统来指定
- 注意;代码最好不要依赖于优先级线程,有可能会造成问题,java中给定的优先级并不一定严格按照给定优先级执行