多线程
Posted cao-yin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
多线程[Thread]
多线程是Java语言的重要特性,大量应用于网络编程、服务器端程序的开发,最常见的UI界面底层原理、操作系统底层原理都大量使用了多线程。
1、程序、进程与线程
程序Program
? 程序是一段静态的代码,指令的集合。
进程Process
? 进程是指一种正在运行的程序,有自己的地址空间,进程是 系统 进行资源分配的最小单位。多个进程之间不共享资源。
进程的特点
? 动态性
? 并发性
? 独立性
并发和并行的区别
? 多个CPU同时执行多个任务
? 一个CPU(采用时间片)同时执行多个任务
线程Thread
? 进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。
? 线程又被称为轻量级进程(lightweight process)
? 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为 多线程
? 一个进程中至少要有一个线程。当然可以有多个线程
? 线程是cpu进行调度执行的最小单位
线程特点
? 轻量级进程
? 独立调度的基本单位
? 可并发执行
? 共享进程资源
线程和进程的区别
Thread类常用方法
2、线程的创建和启动
线程的创建
? 方式1:继承Java.lang.Thread类,重写run() 方法
? 方式2:实现Java.lang.Runnable接口,并实现run() 方法
? 方式3:JUC并发包下,实现Callable。JDK1.5 出现的。
? 方法run( )称为线程体。
线程的启动
? 新建的线程不会自动开始运行,必须通过start( )方法启动
? 不能直接调用run()来启动线程,这样run()将作为一个普通方法立即执行,执行完毕前其他线程无法并发法执行。
? Java程序启动时,会立刻创建主线程,main就是在这个线程上运行。当不再产生新线程时,程序是单线程的。
第一种方式创建线程:
* 1、创建:Thread + 重写run
* 2、启动:创建子类对象 + start()
1 // 继承Thread类 2 public class StartThread extends Thread { 3 // 重写了run方法 4 @Override 5 public void run() { 6 for (int i = 0; i < 5; i++) { 7 System.out.println("一边听音乐"); 8 } 9 } 10 public static void main(String[] args) { 11 // 创建子类对象 12 StartThread thread = new StartThread(); 13 // 启动线程 14 thread.start();// 不保证CPU立即调用 15 // thread.run();// 调用普通方法,不管怎么运行:必须听完音乐才能敲代码 16 for (int i = 0; i < 5; i++) { 17 System.out.println("一边敲代码"); 18 } 19 } 20 }
第二种方式创建线程:
* 1、创建:实现Runnable接口 + 重写run
* 2、启动:创建实现类对象 + Thread对象 + start()
1 // 实现Runnable接口 2 public class StartRun implements Runnable { 3 // 重写了run方法,线程的入口点 4 @Override 5 public void run() { 6 for (int i = 0; i < 5; i++) { 7 System.out.println("一边听音乐"); 8 } 9 } 10 public static void main(String[] args) { 11 /*// 创建实现类对象 12 StartRun sr = new StartRun(); 13 // 创建代理类对象 14 Thread t = new Thread(sr); 15 // 启动 16 t.start();*/ 17 18 // 可简写为 19 new Thread(new StartRun()).start(); 20 21 22 // thread.run();// 调用普通方法,不管怎么运行:必须听完音乐才能敲代码 23 for (int i = 0; i < 5; i++) { 24 System.out.println("一边敲代码"); 25 } 26 } 27 }
两种线程创建方式的比较
? 继承Thread类方式的多线程
? 优势:编写简单
? 劣势:无法继承其它父类
? 实现Runnable接口方式的多线程
? 优势:可以继承其它类,多线程可共享同一个Runnable对象
? 劣势:编程方式稍微复杂,如果需要访问当前线程,需要调用Thread.currentThread()方法
一般Runnable接口方式要优先使用。
example【模拟抢票】
1 package boom.thread; 2 /** 3 * 共享资源:模拟抢票[会出现线程安全] 4 * @author Administrator 5 * 6 */ 7 public class Web12306 implements Runnable{ 8 // 余票 9 private int ticketNums = 100; 10 11 @Override 12 public void run() { 13 while(true){ 14 if(ticketNums < 0){ 15 break; 16 } 17 try { 18 Thread.sleep(100); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 // Thread.currentThread().getName() 当前线程名称 23 System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--); 24 } 25 } 26 public static void main(String[] args) { 27 // 一份资源 28 Web12306 web12306 =new Web12306(); 29 System.out.println(Thread.currentThread().getName()); 30 // 多个代理 31 new Thread(web12306, "售票员-1").start(); 32 new Thread(web12306, "售票员-2").start(); 33 new Thread(web12306, "售票员-3").start(); 34 new Thread(web12306, "售票员-4").start(); 35 new Thread(web12306, "售票员-5").start(); 36 } 37 }
example【龟兔赛跑】
1 package boom.thread; 2 3 import java.io.Reader; 4 5 /** 6 * 模拟龟兔赛跑 7 * 8 * @author Administepsor 9 * 10 */ 11 public class Racer implements Runnable { 12 private static String winner;// 记录胜利者 13 14 @Override 15 public void run() { 16 for (int steps = 1; steps <= 100; steps++) { 17 // 模拟休息[兔子每走十步就休息100毫秒] 18 if (Thread.currentThread().getName().equals("兔子") && steps % 10 == 0) { 19 try { 20 Thread.sleep(100); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 System.out.println(Thread.currentThread().getName() + "-->" + steps); 26 // 比赛是否结束 27 boolean flag = gameOver(steps); 28 if (flag) { 29 break; 30 } 31 } 32 } 33 34 // 比赛是否结束 35 public boolean gameOver(int steps) { 36 if (winner != null) {// 存在胜利者 37 return true; 38 } else { 39 if (steps == 100) { 40 winner = Thread.currentThread().getName(); 41 System.out.println("winner => " + winner); 42 } 43 } 44 return false; 45 } 46 47 public static void main(String[] args) { 48 Racer reader = new Racer(); 49 new Thread(reader, "乌龟").start(); 50 new Thread(reader, "兔子").start(); 51 } 52 53 }
第三种方式:实现Callable接口【先了解】
? 与实行Runnable相比, Callable功能更强大些
? 方法不同
? 可以有返回值,支持泛型的返回值
? 可以抛出异常
? 需要借助FutureTask,比如获取返回结果
Future接口
? 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
? FutrueTask是Futrue接口的唯一的实现类
? FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
example【改进龟兔赛跑】
1 package boom.thread; 2 3 import java.io.Reader; 4 import java.lang.reflect.Executable; 5 import java.util.concurrent.Callable; 6 import java.util.concurrent.ExecutionException; 7 import java.util.concurrent.ExecutorService; 8 import java.util.concurrent.Executors; 9 import java.util.concurrent.Future; 10 11 /** 12 * 模拟龟兔赛跑 13 * 14 * @author Administepsor 15 * 16 */ 17 public class Racer2 implements Callable<Integer> { 18 private static String winner;// 记录胜利者 19 20 @Override 21 public Integer call() throws Exception { 22 for (int steps = 1; steps <= 100; steps++) { 23 // 模拟休息[兔子每走十步就休息100毫秒] 24 if (Thread.currentThread().getName().equals("pool-1-thread-1") && steps % 10 == 0) { 25 Thread.sleep(100); 26 } 27 System.out.println(Thread.currentThread().getName() + "-->" + steps); 28 // 比赛是否结束 29 boolean flag = gameOver(steps); 30 if (flag) { 31 return steps; 32 } 33 } 34 return null; 35 } 36 37 // 比赛是否结束 38 public boolean gameOver(int steps) { 39 if (winner != null) {// 存在胜利者 40 return true; 41 } else { 42 if (steps == 100) { 43 winner = Thread.currentThread().getName(); 44 System.out.println("winner => " + winner); 45 return true; 46 } 47 } 48 return false; 49 } 50 51 public static void main(String[] args) throws InterruptedException, ExecutionException { 52 Racer2 reader = new Racer2(); 53 54 // 创建执行服务 55 ExecutorService ser = Executors.newFixedThreadPool(2); 56 // 提交执行 57 Future<Integer> result1 = ser.submit(reader); 58 Future<Integer> result2 = ser.submit(reader); 59 // 获取结果 60 Integer integer1 = result1.get(); 61 Integer integer2 = result2.get(); 62 System.out.println(integer1 + "-->" + integer2); 63 // 关闭服务 64 ser.shutdownNow(); 65 } 66 67 }
简单的静态代理设计模式
1 package boom.thread; 2 3 public class StaticProxy { 4 public static void main(String[] args) { 5 // new 子类对象传入真是角色在.具体实现方法 6 new WeddingCompany(new You()).happyMarry(); 7 } 8 } 9 // 定义抽象类 10 interface Marry{ 11 // 实现抽象方法 12 void happyMarry(); 13 } 14 // 真实角色继承了抽象类 15 class You implements Marry{ 16 // 重写抽象类的抽象方法 17 @Override 18 public void happyMarry() { 19 System.out.println("我们终于结婚了!!"); 20 } 21 } 22 // 代理角色[婚庆公司] 23 class WeddingCompany implements Marry{ 24 // 真实角色 25 private Marry flag; 26 // 带参构造方法 27 public WeddingCompany(Marry flag) { 28 this.flag = flag; 29 } 30 31 @Override 32 public void happyMarry() { 33 ready(); 34 this.flag.happyMarry(); 35 affter(); 36 } 37 38 private void ready() { 39 System.out.println("在路上!"); 40 } 41 42 private void affter() { 43 System.out.println("民政局拿小本本!!!"); 44 } 45 }
线程的生命周期[线程的五大状态]
新生状态:
? 用new关键字建立一个线程对象后,该线程对象就处于新生状态。
? 处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态
就绪状态:
? 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU
? 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称之为“cpu调度”。
运行状态:
? 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。
? 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
阻塞状态:
? 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
? 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。
死亡状态:
? 死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个。一个是正常运行的线程完成了它的全部工作;另一个是线
程被强制性地终止,如通过执行stop方法来终止一个线程[不推荐使用】,三是线程抛出未捕获的异常.
线程的控制方法
·sleep()
·使线程停止运行一段时间,将处于阻塞状态
·如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!
·join()
·阻塞指定线程等到另一个线程完成以后再继续执行。
·yield()
·让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态;
·调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行!
·setDaemon()
·可以将指定的线程设置成后台线程,守护线程;
·创建用户线程的线程结束时,后台线程也随之消亡;
·只能在线程启动之前把它设为后台线程
·setPriority(int newPriority)getPriority()
·线程的优先级代表的是概率·范围从1到10,默认为5
·stop()停止线程 不推荐使用
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章