六、 Java Thread
1. 基本概念
1. 程序(Program):静态概念
* 程序是指一系列指令的有序集合。指令序列是顺序执行的,直到一条跳转指令(或转移指令)被执行,或者一个中断出现
2. 进程(Process):动态概念
* 狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)
* 广义定义:进程是一个具有一定独立功能的程序关于某数据集合上的一次运行活动
* 进程是系统进行资源分配的基本单位,是操作系统结构的基础
* 通常在一个进程中可以包含若干个线程
3. 线程(Thread):动态概念
* 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位
* 线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享该进程的全部系统资源
* 在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位
* 由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量
* 近年来推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标
4. 时间片(Timeslice)
* 又称处理器片(processor slice),是分时操作系统分配给每个正在运行的进程微观上的一段CPU时间(在抢占内核中是:从进程开始运行直到被抢占的时间)
* 现代操作系统(如:Windows、Linux 等)允许同时运行多个进程 —— 例如,你可以在打开音乐播放器听音乐的同时用浏览器浏览网页并下载文件
* 事实上,由于一台计算机通常只有一个CPU,所以永远不可能真正地同时运行多个任务
* 这些进程“看起来像”同时运行的,实则是轮番穿插地运行,由于时间片通常很短(在 Linux 上为 5ms-800ms),所以用户不会感觉的到
2. 一个线程的生命周期
3. 线程的创建
3.1 继承 Thread 类,Thread 是 Runnable 接口的实现类
/** * 1.创建线程:继承 Thread 类 + 重写 run(); * 2.使用线程:实例化线程对象 + 对象.start(); * * 模拟龟兔赛跑 */ public class Rabbit extends Thread{ @Override public void run(){ //线程体 for(int i=0;i<2;i++){ System.out.println("兔子跑了"+i+"步"); } } } public class Tortoise extends Thread{ @Override public void run(){ //线程体 for(int i=0;i<2;i++){ System.out.println("乌龟跑了"+i+"步"); } } } public class Test{ public static void main(String[] args) { //创建子类对象 Rabbit rab = new Rabbit(); Tortoise tor = new Tortoise(); //调用start方法 rab.start();//不要调用 run 方法,不然就是普通的方法调用 tor.start(); for(int i=0;i<2;i++){ System.out.println("main==>"+i); } } } // 运行结果,每一次运行结果是不同的 main==>0 兔子跑了0步 兔子跑了1步 乌龟跑了0步 乌龟跑了1步 兔子跑了2步 main==>1 兔子跑了3步 乌龟跑了2步 兔子跑了4步 main==>2 兔子跑了5步 乌龟跑了3步 main==>3 乌龟跑了4步 main==>4 乌龟跑了5步 main==>5
3.2 实现 Runnable 接口
/** * 1.实现 Runnable 接口 + 重写 run(); -->真实角色 * 2.启动多线程,使用静态代理 Thread * 1)创建真实角色 * 2)创建代理角色+真实角色引用 * 3)调用.start(); * 3.推荐通过实现 Runnable 接口创建线程: * 1)避免单继承的局限性 * 2)便于共享资源 */ //真实角色 class NewThread implements Runnable{ @Override public void run() { for(int i=0;i<6;i++){ System.out.println("真实角色==>"+i); } } } public class Test { public static void main(String[] args) { //1.创建真实角色 NewThread t = new NewThread(); //2.创建静态代理+真实角色引用 Thread proxy = new Thread(t); //3.调用.start(); 启动线程 proxy.start(); for(int i=0;i<6;i++){ System.out.println("main==>"+i); } } } // 运行结果,每一次运行结果是不同的 main==>0 真实角色==>0 main==>1 真实角色==>1 main==>2 真实角色==>2 main==>3 真实角色==>3 main==>4 真实角色==>4 真实角色==>5 main==>5
3.3 有返回值的线程,通过 Callable 和 Future 创建
/** * FutureTask 是 RunnableFuture 接口的实现类(RunnableFuture 继承于 Runnable 接口,Future 接口) * 1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值 * 2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值 * 3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程 * 4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值 */ public class CallableTest implements Callable<Integer>{ @Override public Integer call() throws Exception { int i = 0; for(; i<10; i++){ System.out.println(Thread.currentThread().getName()+"==>"+i); } return i; } } public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { CallableTest callable = new CallableTest(); FutureTask<Integer> ft = new FutureTask<>(callable); for(int i=0; i<10; i++){ System.out.println(Thread.currentThread().getName()+"==>"+i); if(i==2){ new Thread(ft, "Callable 线程").start(); } } System.out.println("Callable 线程返回值:" + ft.get()); } } // 运行结果不唯一 main==>0 main==>1 main==>2 main==>3 main==>4 main==>5 Callable 线程==>0 main==>6 main==>7 Callable 线程==>1 main==>8 Callable 线程==>2 Callable 线程==>3 Callable 线程==>4 Callable 线程==>5 main==>9 Callable 线程==>6 Callable 线程==>7 Callable 线程==>8 Callable 线程==>9 Callable 线程返回值:10
public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建线程池 ExecutorService pool = Executors.newFixedThreadPool(2); CallableTest callable = new CallableTest(); Future<Integer> f = null; for(int i=0; i<100; i++){ System.out.println("Main==>"+i); if(i==10){ f = pool.submit(callable); //关闭线程池 pool.shutdownNow(); } } //获取返回值 int num = f.get(); System.out.println("返回值为:"+num); } } class CallableTest implements Callable<Integer>{ @Override public Integer call() throws Exception { int i; for(i=0; i<100; i++){ System.out.println(i); } return i; } }
4. Thread 类
// 创建 Thread thread = New Thread(); Thread thread = New Thread(String name); Thread thread = New Thread(Runnable target); Thread thread = New Thread(Runnable target, String name); // 动态方法 thread.isAlive();// 测试线程是否处于活动状态 thread.start();// 开始执行线程,JVM 调用该线程的 run 方法 thread.setName(String name);// 改变线程名称 thread.getName();// 获取线程名称 thread.setPriority(int priority);// 更改线程的优先级 /* 具有较高优先级的线程应该在低优先级的线程之前分配处理器时间,然而,线程优先级不能保证线程执行的顺序,而且非常依赖于平台 Thread.MIN_PRIORITY 1 Thread.NORM_PRIORITY 5(默认) Thread.MAX_PRIORITY 10 */ thread.getPriority();// 获取线程的优先级 thread.setDaemon(boolean on);// 将该线程标记为守护线程。守护线程是服务提供者线程,当 JVM 检测到应用程序中的所有线程都只是守护线程时,它将退出应用程序 thread.isDaemon();// 测试是否为守护线程 thread.join(long millisec);// 等待该线程终止的时间。假设有两个线程 t1 和 t2,如果线程 t1 调用 t2.join(),线程 t1 开始等待,直到线程 t2 终止,t1 才会继续执行 thread.interrupt();// 尝试中断线程 // 静态方法 Thread.currentThread();// 返回对当前正在执行的线程对象的引用 Thread.yield();// 尝试暂停当前正在执行的线程对象,并执行其他线程。该线程放弃当前对处理器的使用 Thread.sleep(long millisec);// 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。如果线程在进入休眠之前拥有锁的所有权,则它在休眠期间继续保持这些锁 Thread.interrupted();// 测试线程是否中断 Thread.holdsLock(Object o);// 返回 boolean 类型,当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true Thread.dumpStack();// 将当前线程的堆栈跟踪打印至标准错误流
5. 停止线程
/** * 停止线程 * 1.自然终止:线程体正常执行完毕 * 2.外部干涉: * 1)在线程类中定义线程体使用的标识,关键字 volatile * 2)线程体使用的标识 * 3)提供对外的方法改变该标识 * 4)外部根据条件调用改变标识的方法即可
*/
public class Study implements Runnable{ //1.线程类中定义线程体使用的标识,关键字 volatile 可以保持线程的工作内存中的变量值与它们在主存储器中的值同步 private volatile boolean flag = true;
@Override public void run() { //2.在线程体使用标识 while(flag){ System.out.println("study thread......"); } } //3.提供对外的方法改变标识 public void stop(){ this.flag = false; } }
public class Test {
public static void main(String[] args) {
Study sd = new Study();
new Thread(sd).start();
//4.外部根据条件调用改变标识的方法即可
for(int i=0;i<100;i++){
if(i==50){
sd.stop();
}
System.out.println("main==>"+i);
}
}
}
6. 线程阻塞
// 线程阻塞1:thread.join();假设有两个线程 t1 和 t2,如果线程 t1 调用 t2.join(),线程 t1 开始等待,直到线程 t2 终止,t1 才会继续执行 public class Test01 extends Thread { public static void main(String[] args) throws InterruptedException { Test01 demo = new Test01(); Thread t = new Thread(demo); t.start(); for(int i=0;i<100;i++){ t.join();// main 阻塞 System.out.println("main==>"+i); } } @Override public void run() { for(int i=0;i<100;i++){ System.out.println("thread...."+i); } } } // 线程阻塞2:Thread.yield();暂停当前正在执行的线程对象,并执行其他线程。该线程放弃当前对处理器的使用 public class Test02 extends Thread { public static void main(String[] args) throws InterruptedException { Test02 demo = new Test02(); Thread t = new Thread(demo); t.start(); for(int i=0;i<100;i++){ Thread.yield();// main 阻塞 System.out.println("main==>"+i); } } @Override public void run() { for(int i=0;i<100;i++){ System.out.println("thread...."+i); } } } // 线程阻塞3:Thread.sleep(); public class Test03 extends Thread { public static void main(String[] args) throws InterruptedException { Test03 demo = new Test03(); Thread t = new Thread(demo); t.start(); for(int i=0;i<100;i++){ Thread.sleep(1000);// main 阻塞 System.out.println("main==>"+i); } } @Override public void run() { for(int i=0;i<100;i++){ System.out.println("thread...."+i); } } }
7. synchronized 同步
同步(synchronized),确保资源安全 --> 线程安全,效率低 1. 线程安全:就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,直到该线程读取完,其他线程才可使用,因而不会出现数据不一致或者数据污染 2. 线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据 3. 同步块:synchronized(引用类型 | this | 类名.class){} 4. 同步方法:修饰符 + synchronized + 返回类型 + 方法名(){} 5. 过多的同步方法可能造成死锁 public class Role implements Runnable{ private volatile boolean flag = true; private int num = 100; @Override public void run() { while(flag){ t1(); } } // 同步块,线程安全,效率低 public void t1(){ synchronized(Role.class){ if(num<=0){ flag = false; return; } try { Thread.sleep(60);// 模拟延迟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } // 同步方法,线程安全,效率低 public synchronized void t2(){ if(num<=0){ flag = false; return; } try { Thread.sleep(60);// 模拟延迟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } // 线程不安全,效率高 public void t3(){ if(num<=0){ flag = false; return; } try { Thread.sleep(60);// 模拟延迟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } public class Test { public static void main(String[] args) { //1.创建真实角色 Role r = new Role(); //2.创建代理,引用真实角色 Thread td1 = new Thread(r,"甲"); Thread td2 = new Thread(r,"乙"); Thread td3 = new Thread(r,"丙"); //3.启动线程 td1.start(); td2.start(); td3.start(); } }
8. ThreadGroup 线程组
/** * 1. 线程总是线程组的成员 * 2. 线程组以树状结构布置 * 3. 线程组可以包含另一个线程组 */ // 创建 ThreadGroup group = new ThreadGroup();// 默认 this.name = "system" ThreadGroup group = new ThreadGroup(String name); ThreadGroup group = new ThreadGroup(ThreadGroup parent, String name); // 添加新线程 Thread thread = new Thread(ThreadGroup group, String name); Thread thread = new Thread(ThreadGroup group, Runnable target); Thread thread = new Thread(ThreadGroup group, Runnable target, String name); // 常用方法 thread.getThreadGroup();// 返回该线程的 ThreadGroup 的引用 group.getName();// 返回该线程组的名称 group.getParent();// 返回该线程组的父线程组,顶层线程组的父级为 null group.activeCount();// 返回该线程组中活动线程数的估计值 group.interrupt();// 中断该线程组的所有线程 group.enumerate(Thread list[]);// 将此线程组及其子组中的每个活动线程复制到指定的数组中,并返回放入数组的线程数
9. 线程池
/** * 1. 背景 * 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源, * 在 Java 中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收, * 所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,这就是"池化资源"技术的产生原因。
* 2. 线程池 * 1)线程池中维护着多个线程,等待着监督管理者分配可并发执行的任务,这避免了在处理短时间任务时创建与销毁线程的代价 * 2)任务调度以执行线程的常见方法是使用同步队列,称作任务队列。池中的线程等待队列中的任务,并把执行完的任务放入完成队列中 * 3)如果线程池中所有的线程都始终保持繁忙,但队列中包含挂起的任务,则线程池将在一段时间后创建另一个辅助线程(但线程的数目永远不会超过最大值,超过最大值的线程可以排队,但他们要等到其他线程完成后才启动)
* 3. 针对线程池,Java 提供了以下类: * 1)java.util.concurrent.Executor 接口,只提供了一个抽象方法:void execute(Runnable command); * 2)java.util.concurrent.Executors 工具类 * 3)java.util.concurrent.ExecutorService 接口,继承于 Executor 接口 * 4)java.util.concurrent.AbstractExecutorService 抽象类,是 ExecutorService 接口的实现类 * 5)java.util.concurrent.ThreadPoolExecutor 类,继承于 AbstractExecutorService 抽象类 */ // 创建 ExecutorService pool = Executors.newFixedThreadPool(int nThreads);// nThreads 线程池中允许的最大线程数
// 常用方法 pool.execute(Runnable<T> task);// 在将来的某个时候执行给定的任务 pool.submit(Runnable<T> task);// 提交要执行的任务,并返回表示该任务的 Future,在成功完成任务后 Future 的 get 方法将返回 null pool.submit(Callable<T> task); pool.awaitTermination(long timeout, TimeUnit unit);// 等待终止的最大时间;timeout 等待的最长时间;unit 时间单位;如果执行器终止返回 true,超时返回 false,如果在等待过程中被中断,则抛出 InterruptedException pool.isTerminated(); pool.shutdown();// 启动有序关闭,在此过程中执行先前提交的任务,但不接受任何新任务;无返回值 pool.shutdownNow();// 尝试停止所有正在积极执行的任务,停止处理等待的任务,并返回等待执行的任务列表 List<Runnable>
pool.isShutdown();