• Java 第二阶段提升编程能力【线程(基础)】


    代码链接:https://download.csdn.net/download/qq_52354698/86402455?spm=1001.2014.3001.5501

    1. 线程相关概念

    1. 程序

    为完成特定任务、用某种语言编写的一组指令集合

    2. 进程

    1. 进程是指运行的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
    2. 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。
      在这里插入图片描述

    3. 什么是线程

    1. 线程由进程创建的,是进程的一个实体
    2. 一个进程可以拥有多个线程

    4. 其他相关概念

    1. 单线程:同一个时刻,只允许执行一个线程

    2. 多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口

    3. 并发:同一个时刻,多个任务交替执行,单核cpu实现的多任务就是并发(一个cpu的电脑,同时打开了qq和迅雷,该cpu在qq和迅雷之间不断切换执行)
      在这里插入图片描述

    4. 并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行
      在这里插入图片描述

    2. 线程基本使用

    1. 创建线程的两种方式

    1. 继承Thread类,重写run方法
    2. 实现Runnable接口,重写run方法

    在这里插入图片描述

    2. 线程应用案例1-继承Thread类

    1. 编写程序,开启一个线程,该线程每隔一秒。在控制台输出“喵喵,我是小猫咪!”
    2. 对上题改进:当输出80次后,结束线程
    package com.qdu.threaduse;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class Thread01 {
    
        public static void main(String[] args) {
    
            Cat cat = new Cat();
            //启动线程
            cat.start();
    
        }
    
    }
    
    //当一个类继承了Thread类,该类就可以看做线程使用
    //我们会重写run方法,写入自己的业务代码
    //run方法是Thread类实现了Runnable接口的run方法
    class Cat extends Thread {
    
        int times = 0;
    
        //重写run方法,写入自己的逻辑
        @Override
        public void run() {
            while (true) {
                System.out.println("喵喵,我是小猫咪!" + times);
                //输出后,让该线程休眠1秒,sleep单位是毫秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                times++;
                //循环退出后,线程也就随之结束
                if (times == 80) {
                    break;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    3. 线程应用案例2-实现Runnable接口

    1. java 是单继承的,在某些情况下一个可能已经继承了一个类了,这时在来继承Thread类方法来创建线程就显然不可能了
    2. java 设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程

    每隔1秒,在控制台输出“Hi!”,输出10次后,自动退出

    package com.qdu.threaduse;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class Thread02 {
    
        public static void main(String[] args) {
    
            Dog dog = new Dog();
            //创建一个Thread对象,把dog对象(实现Runnable),放入Thread
            Thread thread = new Thread(dog);
            thread.start();
    
        }
    
    }
    
    class Dog implements Runnable {
    
        int count = 0;
        @Override
        public void run() {
            while (true){
                System.out.println("Hi!" + (++ count) + " " + Thread.currentThread().getName());
                if (count == 10) break;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    4. 线程应用案例3-多线程执行

    创建两个线程,一个线程每隔1秒输出“hello,world”,输出10次,退出;另一个线程每隔1秒输出“hi!”,输出5次退出。

    package com.qdu.threaduse;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class Thread03 {
    
        public static void main(String[] args) {
    
            T1 t1 = new T1();
            T2 t2 = new T2();
            Thread thread = new Thread(t1);
            Thread thread1 = new Thread(t2);
    
            thread.start();
            thread1.start();
    
        }
    
    }
    
    class T1 implements Runnable {
    
        int count = 0;
    
        @Override
        public void run() {
            while (true) {
                System.out.println("hello,world" + (++count));
                if (count == 10) break;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    class T2 implements Runnable {
    
        int count = 0;
    
        @Override
        public void run() {
            while (true) {
                System.out.println("hi!" + (++count));
                if (count == 5) break;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    在这里插入图片描述

    5. 线程如何理解

    在这里插入图片描述

    3. 继承Thread vs 实现 Runnable 的区别

    1. 从 java 设计来看,通过继承 Thread 或者实现 Runnable 接口来创建线程本质上没有区别,从 jdk 帮助文档我们可以看到 Thread 类本身就实现了 Runnable 接口。
    2. 实现 Runnable 接口方式更适合多个线程共享一个资源的情况,并且避免了单继承的限制。
    T3 t3 = new T3("hello");
    Thread thread1 = new Thread(t3);
    Thread thread2 = new Thread(t3);
    thread1.start();
    thread2.start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 编程模拟三个售票窗口售票100,分别使用继承Thread类实现和实现Runnable接口来实现
    package com.qdu.ticket;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class SellTicket {
    
        public static void main(String[] args) {
    
            SellTicket01 sellTicket01 = new SellTicket01();
            SellTicket01 sellTicket02 = new SellTicket01();
            SellTicket01 sellTicket03 = new SellTicket01();
    
            sellTicket01.start();
            sellTicket02.start();
            sellTicket03.start();
    
        }
    
    }
    
    class SellTicket01 extends Thread {
        private static int ticketNum = 100;//让多个线程共享
    
        @Override
        public void run() {
            while (true) {
                if (ticketNum <= 0) {
                    System.out.println("售票结束...");
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述

    会出现少卖、多买,等不同步的问题

    4. 线程终止

    1. 基本介绍

    1. 当线程完成任务后,会自动退出。
    2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。
    package com.qdu.exit_;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class ThreadExit {
    
        public static void main(String[] args) throws InterruptedException {
    
            T t = new T();
            t.start();
    
            Thread.sleep(1000 * 10);
            t.setLoop(false);
    
        }
    
    }
    
    class T extends Thread {
        private int count = 0;
        private boolean loop = true;
    
        @Override
        public void run() {
            while (loop) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("T 运行中..." + (++count));
            }
        }
    
        public void setLoop(boolean flag) {
            loop = flag;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    5. 线程常用方法

    1. 常用方法第一组

    1. setName:设置线程名称
    2. getName:返回线程名称
    3. start:是该线程开始执行
    4. run:调用线程对象run方法
    5. setPriority:更改线程的优先级
    6. getPriority:获取线程的优先级
    7. sleep:休眠线程(单位毫秒)
    8. interrupt:中断线程

    2. 注意事项和细节

    1. start 底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程。
    2. 线程优先级范围
    3. interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
    4. sleep,线程的静态方法,使当前线程休眠

    3. 常用方法第二组

    1. yield:线程的礼让。让出cpu,让其它线程执行,但礼让的时间不确定,所以也不一定礼让成功
    2. join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有任务

    4. 用户线程和守护线程

    1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
    2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
    3. 常见的守护线程:垃圾回收机制
    xxx.setDaemon(true);
    
    • 1

    6. 线程的生命周期

    1. JDK 中用 Thread.State 枚举表示了线程的几种状态

    在这里插入图片描述

    2. 线程状态转换图

    在这里插入图片描述

    7. Synchronized

    1. 线程同步机制

    1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
    2. 也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以再对这个内存地址进行操作,知道该线程完成操作,其他线程才能对该内存地址进行操作。

    2. 同步具体方法-Synchronized

    1. 同步代码块
    synchronized (对象) {
    	//需要被同步代码
    }
    
    • 1
    • 2
    • 3
    1. 同步方法
    public synchronized void m(String name) {
    	//需要被同步的代码
    }
    
    • 1
    • 2
    • 3

    3. Synchronized 解决售票问题

    package com.qdu.syn;
    
    /**
     * @author dell
     * @version 1.0
     */
    public class SellTicket {
    
        public static void main(String[] args) {
    
    //        SellTicket01 sellTicket01 = new SellTicket01();
    //        SellTicket01 sellTicket02 = new SellTicket01();
    //        SellTicket01 sellTicket03 = new SellTicket01();
    //
    //        sellTicket01.start();
    //        sellTicket02.start();
    //        sellTicket03.start();
    
            SellTicket03 sellTicket03 = new SellTicket03();
            new Thread(sellTicket03).start();
            new Thread(sellTicket03).start();
            new Thread(sellTicket03).start();
    
        }
    
    }
    
    class SellTicket03 implements Runnable {
        private static int ticketNum = 100;//让多个线程共享
        private boolean loop = true;
    
        public synchronized void sell() {//同步方法
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
    
        }
    
        @Override
        public void run() {
            while (loop) {
                sell();
            }
        }
    }
    
    class SellTicket01 extends Thread {
        private static int ticketNum = 100;//让多个线程共享
    
        @Override
        public void run() {
            while (true) {
                if (ticketNum <= 0) {
                    System.out.println("售票结束...");
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
            }
        }
    }
    
    class SellTicket02 implements Runnable {
        private static int ticketNum = 100;//让多个线程共享
    
        @Override
        public void run() {
            while (true) {
                if (ticketNum <= 0) {
                    System.out.println("售票结束...");
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    在这里插入图片描述

    8. 分析同步原理

    在这里插入图片描述

    9. 互斥锁

    1. 基本介绍

    1. Java 语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
    2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
    3. 关键字 synchronized 来与对象的互斥锁联系。当某个对象用 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问。
    4. 同步的局限性:导致程序的执行效率低。
    5. 同步烦烦烦(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)。
    6. 同步方法(静态的)的锁为当前类本身。
        public synchronized void sell() {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
        public /*synchronized*/ void sell() {
            synchronized (this) {
                if (ticketNum <= 0) {
                    System.out.println("售票结束...");
                    loop = false;
                    return;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + ",剩余票数 " + (--ticketNum));
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2. 注意事项和细节

    1. 同步方法如果没有使用static修饰:默认锁对象为this
    2. 如果方法使用static修饰,默认锁对象:当前类.class
    3. 实现的落地步骤:
      需要分析上锁的代码
      选择同步代码块或同步方法
      要求多个线程的锁对象为同一个即可

    10. 线程的死锁

    1. 基本介绍

    多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,在编程中一定要避免死锁的发生。

    2. 应用案例

    妈妈:你先完成作业,才让你玩手机
    小明:你先让我玩手机,我再写作业

    11. 释放锁

    1. 下面操作会释放锁

    1. 当前线程的同步方法、同步代码块执行结束。
    2. 当前线程在同步代码块、同步方法中遇到break、、return。
    3. 当前线程在同步代码块、同步方法中出现了未处理的Erroe或Exception,导致异常结束。
    4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

    2. 下面操作不会释放锁

    1. 线程执行同步代码块、同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。
    2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放。
  • 相关阅读:
    python之代理ip的配置与调试
    搞定ESD(一):静电放电测试标准解析
    Rocket MQ Crash-Safe机制浅析
    南京邮电大学高级语言程序设计实验三(函数实验)
    力扣 -- 879. 盈利计划(二维费用的背包问题)
    Visual Studio 错误CS0006:未能找到元数据文件踩坑记录
    共享旅游卡:打开0费用旅游新纪元,探索40+精彩线路
    帝国cms后台登录系统限制次数,60分钟过后重新登录解决办法
    Springboot项目中加载Groovy脚本并调用其内部方代码实现
    铁路订票平台小程序|基于微信小程序的铁路订票平台小程序设计与实现(源码+数据库+文档)
  • 原文地址:https://blog.csdn.net/qq_52354698/article/details/126372076