Java基础三
定义
百度百科
线程 有时被称为轻量级进程(Lightweight Process,LWP) ,是程序 执行流的最小单元。一个标准的线程由线程ID,
当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
快速理解
线程是进程内的执行单元,不可再分。
基本操作
流程图
graph LR
F(TIMED_WAITING) -->|notify通知| B
B --> |wait等待|F
A(NEW) --> |start 启动|B{RUNNABLE}
B -->|结束|C(TERNINATED)
B -->|同步块| D(BLOCKED)
D -->|Synchronized| B
B -->|wait等待| E(WAITING)
E -->|notify通知| B
代码操作
//创建一个线程
Thread thread = new Thread();
//启动一个线程
thread.start();
线程启动里面,有个误区。不可以直接调用run()方法,而是借助start()方法。
//错误的调用,不能开启一个线程。
thread.run();
开启一个线程,有两种方式。
1. 调用Thread类实例的start()方法
2. 实现Runna接口,实现run()方法
其实,Thread类run()方法实现的也是Runable接口。
//可以直接重载run()方法来实例化一个Thread类
Thread thread = new Thread(){
@Override
public void run(){
System.out.println("Hello,Thread!");
}
};
//开启线程
thread.start();
线程终止
Thread.stop()
可以直接在代码中调用stop()方法,来进行终止。不推荐使用这个方式,来终止线程,这个方法目前已经被JDK弃用。
因为,一个程序一旦调用它,这个线程就会在调用它的地方强行停止正在做的任务,这就会导致一个线程还没有完成一个完整周期的任务。
类比:战机正在执行任务,这时接到命令,停止任务的执行,返回基地。如果,调用的是stop()方法,战机直接停止执行任务,但是停止执行任务之后,战机该做什么,在这个方法情况下:战机会直接消失。
示例:有两个线程,读线程R,写线程W,W对临界区加锁(Lock),并对变量进行赋值写入,这是stop(),没有赋值写入的字段会被R读到,会引发系统不稳定。
中断
中断线程,推荐使用。
// 中断线程
public void Thread.interrupt()
// 判断是否被中断
public boolean Thread.isInterrupted()
// 判断是否被中断,并清除当前中断状态
public static boolean Thread.interrupted()
写个小代码,实验一把。
这段代码是直接重载线程类中的run()方法。可以按照上面写的代码编写。
@Overrid
public void run(){
// 无限循环
while(true){
// 判断当前线程是否被中断
if(Thread.currentThread().isInterrupted()){
System.out.println("Hello Interrupted!");
break;
}
try{
// 休眠5s ,这里模仿线程做任务
Thread.sleep(5000);
}
// 线程会抛出这个异常,是因为当线程休眠是如果有线程中断这个线程
// 就会抛出这个异常,来中断休眠并中断线程。
catch(InterruptedException e){
System.out.println("Hello InterruptedException.");
// 清除中断状态
Thread.currentThread().interrupt();
}
}
}
挂起和继续执行
百度百科
挂起 (等待,阻塞)进程在操作系统中可以定义为暂时被淘汰出内存的进程,机器的资源是有限的,在资源不足的情况下,操作系统对在内存中的程序进行合理的安排,其中有的进程被暂时调离出内存,当条件允许的时候,会被操作系统再次调回内存,重新进入等待被执行的状态即就绪态,系统在超过一定的时间没有任何动作。[
继续执行,字面意思。挂起是Thread.suspend(),不会释放锁。挂起的线程直到别的线程调用此线程的resume()方法,此线程才会继续执行。如果线程A会调用suspend(),线程B会调用线程A的resume(),线程B的resume()调用发生在线程A的suspend()调用之前,就会导致A线程所持有的锁不会被释放,从而造成死锁。目前已被JDK弃用。
等待线程结束
join()方法,线程A生成,起动另一个线程B,线程B进行大量的耗时的运算,线程A将会在线程B之前结束。这不是想要的结果,因为线程A处理完其他的事务后,需要用到线程B的处理结果,这时候就要用到join()方法,令线程A等待线程B结束之后,在执行。
// join的本质
while(isAlive()){
wait(0);
}
例子代码
public class ThreadA extend Thread{
@Override
public void run(){
ThreadB threadB = new ThreadB();
threadB.start();
//等待线程B
threadB.join();
//休眠2s
Thread.sleep(2000s);
}
public static class ThreadB{
@Override
public void run(){
//休眠5s
Thread.sleep(5000s);
}
}
}
谦让(yeild)
当其他线程来竞争临界区,调用这个方法的线程会主动释放占有的CPU,资源释放,并和其他线程一起参与竞争。
守护线程
百度百科
守护进程 (Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
例子:GC线程,JIT线程
当JVM中所用线程都执行完成后(除了守护线程),只剩下守护线程时,JVM就会自然退出。
Thread thread = new Thread();
//设置一个线程为守护线程,要在start()调用之前设置,否则会报错。
thread.setDaemon(true);
thread.start();
优先级
优先级越高的线程获得CPU执行权的概率越高。
三个等级
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
线程同步操作
synchronized
百度百科
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
自定义理解
加锁,一个房间相当于临界区,给这个房间加锁,配一把玥匙,同时只有一个人可以进入。直接作用于实例方法上,相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。直接作用于静态方法上,相当于对当前类加锁,进入同步代码前要获得当前类的锁。用于保护共享数据。
例子
@Override
public void run(){
Object obj = new Object();
// 把锁加在实例对象obj上
synchronized(obj){
System.out.println("Hello Synchronized.");
}
}
//作用在普通方法上
public synchronized void method(){
// do something...
}
//相当于
public void method(){
synchronized(this){
//do something...
}
}
wati() notify()
注意: 这两个方法是Object类提供的。
举例说明
两个线程A,B。
线程A | 线程B |
---|---|
取得object监视器 | · |
object.wait() | · |
释放object监视器 | · |
· | 取得object监视器 |
· | object.notify() |
等待object监视器 | 释放object监视器 |
重获object监视器 | |
继续执行 |
notify()唤醒一个,notifyAll()唤醒全部在此对象上等待的线程