线程与进程
什么是线程?
一个顺序的程序执行流程就是一个线程,线程是进程中执行运算的最小单位。
什么是进程?
进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。
二者有何异同?
二者最本质的区别在于进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
线程是进程的一部分,一个进程至少有一个线程。
在内存中,每个进程都有独立的地址空间,进程之间的切换会有较大的开销;线程可以看做轻量级的进程,同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。
多线程
多线程:多个单一顺序执行的流程并发运行。造成"感官上同时运行"的效果。
多个线程共享进程的堆和方法区 (元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。
什么是并发?
多个线程实际运行是走走停停的。线程调度程序会将CPU运行时间划分为若干个时间片段并
尽可能均匀的分配给每个线程,拿到时间片的线程被CPU执行这段时间。当超时后线程调度
程序会再次分配一个时间片段给一个线程使得CPU执行它。如此反复。由于CPU执行时间在
纳秒级别,我们感觉不到切换线程运行的过程。所以微观上走走停停,宏观上感觉一起运行
的现象称为并发运行!
线程的生命周期图
如上图所示,线程的生命周期包含5个状态:新建、就绪、运行、阻塞、死亡
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的创建与启动
线程的创建
方式一:继承Thread并重写run方法
定义一个线程类,重写run方法,在其中定义线程要执行的任务(希望和其他线程并发执行的任务)。
注:启动该线程要调用该线程的start方法,而不是run方法!!!
package thread;
public class ThreadDemo1 {
public static void main(String[] args) {
//创建两个线程
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
/*
线程第一次被分配到时间后会执行它的run方法开始工作。
*/
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
public void run(){
for (int i=0;i<1000;i++){
System.out.println("hello姐~");
}
}
}
class MyThread2 extends Thread{
public void run(){
for (int i=0;i<1000;i++){
System.out.println("来了~老弟!");
}
}
}
优点:
在于结构简单,便于匿名内部类形式创建。
缺点:
1:直接继承线程,会导致不能在继承其他类去复用方法,这在实际开发中是非常不便的。
2:定义线程的同时重写了run方法,会导致线程与线程任务绑定在了一起,不利于线程的重用。
方式二:实现Runnable接口单独定义线程任务
package thread;
public class ThreadDemo2 {
public static void main(String[] args) {
//实例化任务
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
//创建线程并指派任务
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable{
public void run() {
for (int i=0;i<1000;i++){
System.out.println("你是谁啊?");
}
}
}
class MyRunnable2 implements Runnable{
public void run() {
for (int i=0;i<1000;i++){
System.out.println("开门!查水表的!");
}
}
}
第二种方式解决了第一种方式的单继承不灵活的问题,是最为广泛的线程创建方式。
线程的优先级
当一个线程调用start方法后就纳入到了线程调度器中被统一管理。此时线程只能通过被动分配CPU时间片的方式来继续并发运行,不能主动索取时间片;调度器会尽可能均匀的分配时间片给每一个线程,但是不保证"一人一次";调整线程的优先级可以最大程度改善获取时间片的概率。原则上优先级越商的线程获取时间片的次数越多。
线程的优先级分为10个等级,以整数1-10表示。其中1为最低优先级,5为默认优先级,10为最高优先级。可以通过线程提供的方法setPriority()进行设置。
Thread常用方法
以下为Thread(线程)类的部分源码,由此可以知晓线程的常用方法。
public class Thread implements Runnable {
// 线程名字
private volatile String name;
// 线程优先级(1~10)
private int priority;
// 守护线程
private boolean daemon = false;
// 线程id
private long tid;
// 线程组
private ThreadGroup group;
// 预定义3个优先级
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
// 构造函数
public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(Runnable target, String name);
// 线程组
public Thread(ThreadGroup group, Runnable target);
// 返回当前正在执行线程对象的引用
public static native Thread currentThread();
// 启动一个新线程
public synchronized void start();
// 线程的方法体,和启动线程没毛关系
public void run();
// 让线程睡眠一会,由活跃状态改为挂起状态
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;
// 打断线程 中断线程 用于停止线程
// 调用该方法时并不需要获取Thread实例的锁。无论何时,任何线程都可以调用其它线程的interruptf方法
public void interrupt();
public boolean isInterrupted()
// 线程是否处于活动状态
public final native boolean isAlive();
// 交出CPU的使用权,从运行状态改为挂起状态
public static native void yield();
public final void join() throws InterruptedException
public final synchronized void join(long millis)
public final synchronized void join(long millis, int nanos) throws InterruptedException
// 设置线程优先级
public final void setPriority(int newPriority);
// 设置是否守护线程
public final void setDaemon(boolean on);
// 线程id
public long getId() { return this.tid; }
// 线程状态
public enum State {
// new 创建
NEW,
// runnable 就绪
RUNNABLE,
// blocked 阻塞
BLOCKED,
// waiting 等待
WAITING,
// timed_waiting
TIMED_WAITING,
// terminated 结束
TERMINATED;
}
}