多线程

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 }
Web12306 Code

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 }
View Code

第三种方式:实现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 }
View Code

简单的静态代理设计模式

技术分享图片
 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 }
View Code

线程的生命周期[线程的五大状态]
技术分享图片

新生状态:
 ? 用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()停止线程 不推荐使用

 

 



 

 

 

 

 

 

 



 





































































































以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程