进程与线程
进程
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
线程
线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
一个正在运行的软件(如迅雷)就是一个进程,一个进程可以同时运行多个任务( 迅雷软件可以同时下载多个文件,每个下载任务就是一个线程), 可以简单的认为进程是线程的集合。
并行与并发:
- 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
- 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
线程的状态
- 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
- 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
- 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
- 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成
同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
创建线程
- 继承Thread类创建线程
public class MyThread extends Thread{//继承Thread类 public void run(){ //重写run方法 } } public class Main {undefined public static void main(String[] args){undefined new MyThread().start();//创建并启动线程 } } 复制代码
1】定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2】创建Thread子类的实例,也就是创建了线程对象
3】启动线程,即调用线程的start()方法
- 实现Runnable接口创建线程
public class MyThread implements Runnable {//实现Runnable接口 public void run(){ //重写run方法 } } public class Main {undefined public static void main(String[] args){undefined //创建并启动线程 MyThread2 myThread=new MyThread2(); Thread thread=new Thread(myThread); thread().start(); } } 复制代码
1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3】第三部依然是通过调用线程对象的start()方法来启动线程
- 使用Callable和Future创建线程
public static class MyThread implements Callable{ @Override public Object call() throws Exception { return 1; } } public class Main {undefined public static void main(String[] args){undefined MyThread3 th=new MyThread3(); //也可以直接使用Lambda表达式创建Callable对象 //使用FutureTask类来包装Callable对象 FutureTask<Integer> future=new FutureTask<Integer>( (Callable<Integer>)()->{ return 1; } ); new Thread(future,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程 try{ System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回 }catch(Exception e){undefined ex.printStackTrace(); } } }
1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
- 使用线程池例如用Executor框架
public static void main(String args[]) throws InterruptedException{ RejectedExecutionHandlerImpl rejectionHandler = new RejectedExecutionHandlerImpl(); ThreadFactory threadFactory = Executors.defaultThreadFactory(); ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), threadFactory, rejectionHandler); MyMonitorThread monitor = new MyMonitorThread(executorPool, 3); Thread monitorThread = new Thread(monitor); monitorThread.start(); for(int i=0; i<10; i++){ executorPool.execute(new WorkerThread("cmd"+i)); } Thread.sleep(30000); executorPool.shutdown(); Thread.sleep(5000); monitor.shutdown();
![](C:\Users\zmj\Pictures\Camera Roll\threadPool.webp)
1 如果此时线程池中的数量小于 corePoolSize(核心池的大小) , 即使线程池中的线程都处于空闲状态, 也要创建新的线程来处理被添加的任务(也就是每来一个任务, 就要创建一个线程来执行任务) 。2 如果此时线程池中的数量大于等于 corePoolSize, 但是缓冲队列workQueue 未满, 那么任务被放入缓冲队列, 则该任务会等待空闲线程将其取出去执行。3 如果此时线程池中的数量大于等于 corePoolSize , 缓 冲 队 列workQueue 满, 并且线程池中的数量小于 maximumPoolSize(线程池最大线程数) , 建新的线程来处理被添加的任务。4 如果 此时 线程 池中 的数量 大 于 等 于 corePoolSize, 缓 冲 队列workQueue 满, 并且线程池中的数量等于 maximumPoolSize, 那么通过RejectedExecutionHandler 所指定的策略(任务拒绝策略)来处理此任务。也就是处理任务的优先级为: 核心线程 corePoolSize、 任务队列workQueue、 最大线程 maximumPoolSize, 如果三者都满了, 使用handler 处理被拒绝的任务。
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; } }
start() 与 run()
start(): 启动一个线程,线程之间是没有顺序的,是按CPU分配的时间片来回切换的。
run(): 调用线程的run方法,就是普通的方法调用,虽然将代码封装到两个线程体中,可以看到线程中打印的线程名字都是main主线程,run()方法用于封装线程的代码
sleep() 与 interrupt()
sleep(): 睡眠指定时间,即让程序暂停指定时间运行,时间到了会继续执行代码,如果时间未到就要醒需要使用interrupt()来随时唤醒 interrupt(): 唤醒正在睡眠的程序,调用interrupt()方法,会使得sleep()方法抛出InterruptedException异常,当sleep()方法抛出异常就中断了sleep的方法,从而让程序继续运行下去
wait() 与 notify()
wait(): 导致线程进入等待阻塞状态,会一直等待直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。wait(long timeout): 时间到了自动执行,类似于sleep(long millis) notify(): 该方法只能在同步方法或同步块内部调用, 随机选择一个(注意:只会通知一个)在该对象上调用wait方法的线程,解除其阻塞状态 notifyAll(): 唤醒所有的wait对象