多线程
2021-06-22 20:05
标签:系统 启动 不能 getname RoCE display 民政局 catch view 多线程[Thread] 多线程是Java语言的重要特性,大量应用于网络编程、服务器端程序的开发,最常见的UI界面底层原理、操作系统底层原理都大量使用了多线程。 线程Thread Thread类常用方法 * 1、创建:Thread + 重写run 第二种方式创建线程: 两种线程创建方式的比较 example【龟兔赛跑】 第三种方式:实现Callable接口【先了解】 简单的静态代理设计模式 线程的生命周期[线程的五大状态] 新生状态: 多线程 标签:系统 启动 不能 getname RoCE display 民政局 catch view 原文地址:https://www.cnblogs.com/cao-yin/p/9670800.html
1、程序、进程与线程
程序Program
? 程序是一段静态的代码,指令的集合。
进程Process
? 进程是指一种正在运行的程序,有自己的地址空间,进程是 系统 进行资源分配的最小单位。多个进程之间不共享资源。
进程的特点
? 动态性
? 并发性
? 独立性
并发和并行的区别
? 多个CPU同时执行多个任务
? 一个CPU(采用时间片)同时执行多个任务
? 进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。
? 线程又被称为轻量级进程(lightweight process)
? 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为 多线程
? 一个进程中至少要有一个线程。当然可以有多个线程
? 线程是cpu进行调度执行的最小单位
线程特点
? 轻量级进程
? 独立调度的基本单位
? 可并发执行
? 共享进程资源
线程和进程的区别
2、线程的创建和启动
线程的创建
? 方式1:继承Java.lang.Thread类,重写run() 方法
? 方式2:实现Java.lang.Runnable接口,并实现run() 方法
? 方式3:JUC并发包下,实现Callable。JDK1.5 出现的。
? 方法run( )称为线程体。
线程的启动
? 新建的线程不会自动开始运行,必须通过start( )方法启动
? 不能直接调用run()来启动线程,这样run()将作为一个普通方法立即执行,执行完毕前其他线程无法并发法执行。
? Java程序启动时,会立刻创建主线程,main就是在这个线程上运行。当不再产生新线程时,程序是单线程的。
第一种方式创建线程:
* 2、启动:创建子类对象 + start() 1 // 继承Thread类
2 public class StartThread extends Thread {
3 // 重写了run方法
4 @Override
5 public void run() {
6 for (int i = 0; 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 ) {
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 ) {
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 ) {
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 ){
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 }
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 ) {
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 }
? 与实行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
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()停止线程 不推荐使用