• 第二十三章 多线程(一)


    1. 多线程相关概念

    1.1 并发(Concurrent)

            在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行;

            同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把CPU时间分成若干段,使多个进程快速交替的执行。

    1.2 并行(Parallel)

             当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,称之为并行;决定并行的因素是CPU核心数量,一个CPU多个核心也可以并行。

    1.3 进程(Process)

            正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源(CPU(寄存器)、IO、内存、网络资源等);同一个程序,同一时刻被两次运行,那么他们就是两个独立的进程。

     1.4 线程(Thread)

            操作系统能够进行运算调度的最小单位;它被包含在进程中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    1.5 多线程的优缺点

    优点:

            ①可以让程序运行速度更快,用户在频繁切换运行进程的界面,界面下运行进程底层都有多个线程不断运行着,CPU一直处于运行状态;

            ②提高CPU利用率,大量线程同时处于运行状态,让CPU不间断运行;

    缺点: 

            ①多线程情况,多个线程可能共享一个资源,如果资源数量有限,多线程竞争会产生等待问题;

            ②多线程,不断进行线程(上下文)切换,线程切换消耗资源;

            ③多线程,可能造成死锁。

    2. 多线程实现方式

    2.1 继承Thread类

    1. public class MyThread extends Thread{
    2. private int ticket = 20;
    3. String name;
    4. public MyThread(String name) {
    5. this.name = name;
    6. }
    7. /**
    8. * 重写run方法,实现多线程执行业务的方法
    9. */
    10. @Override
    11. public void run() {
    12. while(ticket>0){
    13. System.out.println(Thread.currentThread().getName()+" "+this.name+"卖出一张票,剩余:\t^"+(--ticket)+"^\t"+ LocalDateTime.now());
    14. }
    15. }
    16. }

    2.2 实现Runnable接口

    Runnable接口可由任何类实现,其实例将由线程执行;必须定义无参方法run。

    1. public class MyRunnable implements Runnable{
    2. private int ticket=20;
    3. @Override
    4. public void run() {
    5. while (ticket>0){
    6. System.out.println(Thread.currentThread().getName()+"卖出一张票,剩余:\t^"+(--ticket)+"^\t"+ LocalDateTime.now());
    7. }
    8. }
    9. }

    2.3 实现Callable接口

    类似于Runnable接口,但Runnable不能返回结果,也不能抛出被检查的异常。

    1. public class MyCallable implements Callable {
    2. /**
    3. * 线程的业务方法
    4. * @return
    5. * @throws Exception
    6. */
    7. @Override
    8. public Object call() throws Exception {
    9. int sum = 0;
    10. for (int i = 0; i < 5; i++) {
    11. System.out.println(Thread.currentThread().getName()+"\t^"+i+"^\t"+ LocalDateTime.now());
    12. sum+=i;
    13. }
    14. return sum;
    15. }
    16. }

     测试类:

    1. public class MyTest {
    2. public static void main(String[] args) throws ExecutionException, InterruptedException {
    3. MyThread myThread = new MyThread("张三");
    4. myThread.start();
    5. MyRunnable myRunnable = new MyRunnable();
    6. Thread thread = new Thread(myRunnable);
    7. thread.start();
    8. MyCallable myCallable = new MyCallable();
    9. FutureTask ft = new FutureTask(myCallable);
    10. new Thread(ft).start();
    11. int res = (int)ft.get();
    12. System.out.println("++++++"+res+"+++++");
    13. }
    14. }

    2.4 线程池实现多线程

            Executors工厂和工具类Executor,ExecutorService,ScheduleExecutorService,ThreadFactory和Callable在此包中定义的类支持以下几种方法:

    ①创建并返回一个ExecutorService设置的常用的配置设置的方法;

    ②创建并返回一个ScheduledExecutorService的方法,其中设置了常用的配置设置;

    ③创建并返回"包装"ExecutorService的方法,通过使实现特定的方法无法访问来禁用重新配置;

    ④创建并返回将新创建的线程设置为已知状态的ThreadFactory的方法;

    ⑤创建并返回一个方法Callable出的其他闭包形式,这样就可以在需要的执行方法使用Callable。

    1. public class MyTest2 {
    2. public static void main(String[] args) {
    3. MyThread myThread1 = new MyThread("张三");
    4. MyThread myThread2 = new MyThread("李四");
    5. MyThread myThread3 = new MyThread("王五");
    6. //启动多线程
    7. ExecutorService executorService = Executors.newFixedThreadPool(3);
    8. executorService.execute(myThread1);
    9. executorService.execute(myThread2);
    10. executorService.execute(myThread3);
    11. //关闭线程池
    12. executorService.shutdown();
    13. }
    14. }

    2.5 实现runnable和继承thread的区别

            受Java单继承影响,继承thread无法实现成员变量(不使用特殊手段,加static),实现Runnable接口时,线程运行可以共享成员变量。

    2.6 多线程常用方法

    currentThread():返回对当前正在执行的线程对象的引用;

    getName():返回此线程的名称;

    run():当前启动的线程,执行业务的地方;

    start():启动线程,让线程处于就绪状态;

    setPriority():更改此线程的优先级,优先级是1~10之间的任意数字,默认值是5。

    1. public class MyTest3 {
    2. public static void main(String[] args) {
    3. IA ia =new IA() {
    4. @Override
    5. public void test() {
    6. System.out.println("wan ^^");
    7. }
    8. };
    9. ia.test();
    10. Thread thread1 = new Thread("t1"){
    11. @Override
    12. public void run() {
    13. for (int i = 0; i < 10; i++) {
    14. System.out.println(Thread.currentThread().getName()+"对所有的烦恼说拜拜!"+"^"+i+"^");
    15. if(i==5){
    16. Thread.yield();
    17. }
    18. }
    19. }
    20. };
    21. Thread thread2 = new Thread("t2"){
    22. @Override
    23. public void run() {
    24. for (int i = 0; i < 10; i++) {
    25. System.out.println(Thread.currentThread().getName()+"对所有的烦恼说拜拜!"+"^"+i+"^");
    26. if(i%2==0){
    27. Thread.yield();
    28. }
    29. }
    30. }
    31. };
    32. Thread thread3 = new Thread("t3"){
    33. @Override
    34. public void run() {
    35. for (int i = 0; i < 10; i++) {
    36. System.out.println(Thread.currentThread().getName()+"对所有的烦恼说拜拜!"+"^"+i+"^");
    37. if(i>0){
    38. Thread.yield();
    39. }
    40. }
    41. }
    42. };
    43. thread2.setPriority(Thread.MIN_PRIORITY);
    44. thread3.setPriority(Thread.MAX_PRIORITY);
    45. thread1.start();
    46. thread2.start();
    47. thread3.start();
    48. }
    49. }

     2.7 多线程的状态

    五种状态:

            新建(程序还没有开始运行线程中的代码)

            就绪(start方法返回之后就处于就绪状态,不一定直接运行run需要同其他线程竞争CPU时间)

            运行(线程获得CPU时间后,进入运行状态,执行run)

            阻塞(等待wait、带超时的等待sleep)

           终止(死亡,正常退出或者异常终止)

    3. 综合练习

            使用Java程序,ping ip, 查看每个ip是否可以ping通;

    1. package com.util;
    2. import java.io.BufferedReader;
    3. import java.io.IOException;
    4. import java.io.InputStream;
    5. import java.io.InputStreamReader;
    6. import java.nio.Buffer;
    7. /**
    8. * @author :muxiaowen
    9. * @date : 2022/9/16 16:45
    10. */
    11. public class PingUtil {
    12. public static long pingIp(String ip){
    13. long start = System.currentTimeMillis();
    14. System.out.println("------------");
    15. System.out.println(Thread.currentThread().getName());
    16. System.out.println("start:"+start);
    17. long end = 0;
    18. InputStream inputStream = null;
    19. BufferedReader br =null;
    20. try {
    21. //程序运行是的工具类
    22. Runtime runtime = Runtime.getRuntime();
    23. //执行cmd命令
    24. Process exec = runtime.exec("ping " + ip);
    25. //获取字节流
    26. inputStream = exec.getInputStream();
    27. //构建缓冲字符流
    28. br = new BufferedReader(new InputStreamReader(inputStream));
    29. String str= "";
    30. while ((str=br.readLine())!=null){
    31. System.out.println(str);
    32. if(str.contains("TTL")){
    33. end = System.currentTimeMillis();
    34. System.out.println(ip+"可以ping通");
    35. System.out.println("end:"+end);
    36. return end-start;
    37. }
    38. if(str.contains("Destination host unreachable")||str.contains("Request timed out")){
    39. System.out.println(ip+"不可以ping通");
    40. end = System.currentTimeMillis();
    41. System.out.println("end:"+end);
    42. return end-start;
    43. }
    44. }
    45. end = System.currentTimeMillis();
    46. System.out.println("end:"+end);
    47. } catch (IOException e) {
    48. e.printStackTrace();
    49. }finally {
    50. if(inputStream!=null){
    51. try {
    52. inputStream.close();
    53. } catch (IOException e) {
    54. e.printStackTrace();
    55. }
    56. }
    57. if(br!=null){
    58. try {
    59. br.close();
    60. } catch (IOException e) {
    61. e.printStackTrace();
    62. }
    63. }
    64. }
    65. return end-start;
    66. }
    67. }
    1. public class MyPing implements Callable {
    2. private String ip;
    3. public MyPing(String ip) {
    4. this.ip = ip;
    5. }
    6. @Override
    7. public Object call() throws Exception {
    8. return PingUtil.pingIp(ip);
    9. }
    10. }

    测试类:

    1. public class MyTest6 {
    2. public static void main(String[] args) throws ExecutionException, InterruptedException {
    3. long sum = 0;
    4. ExecutorService executorService = Executors.newFixedThreadPool(10);
    5. for (int i = 0; i <= 120; i++) {
    6. MyPing myPing = new MyPing("192.168.0."+i);
    7. FutureTask ft = new FutureTask(myPing);
    8. executorService.execute(ft);
    9. sum += (long)ft.get();
    10. }
    11. System.out.println("+++++++++++++++++");
    12. System.out.println(sum);
    13. executorService.shutdown();
    14. }
    15. }

    不使用多线程速度会非常慢!

  • 相关阅读:
    MySQL之短时间提高性能的措施
    FPGA设计时序约束九、others类约束之Group Path
    WPF使用TextBlock实现查找结果高亮显示
    个人散户如何参与程序化交易?
    Linux 文件系统Ramfs, rootfs and initramfs
    vue3——element-plus 实现页面刷新之后,保持菜单的激活状态
    搭建私有Git服务器:GitLab部署详解
    shell脚本数组
    自学WEB后端01-安装Express+Node.js框架完成Hello World!
    [HDLBits] Exams/2014 q3fsm
  • 原文地址:https://blog.csdn.net/m0_71674778/article/details/126889873