• Java多线程机制


    目录

    进程与线程

    多线程 

    主线程(main线程)

    线程的创建和启动

    线程的状态 

    线程优先级 

    线程调度 

              线程休眠

     线程的强制运行

    线程的礼让

    同步方法

    同步代码块


    进程与线程

            线程不是进程,但其行为很像进程,线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程。

            和进程可以共享操作系统的资源类似,线程间也可以共享进程中的某些内存单元(包括代码与数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作,但与进程不同的是,线程的中断与恢复可以更加节省系统的开销。

            具有多个线程的进程能更好地表达和解决现实世界的具体问题,多线程是计算机应用开发和程序设计的一项重要的实用技术。

            没有进程就不会有线程,就像没有操作系统就不会有进程一-样。尽管线程不是进程,但在许多方面它非常类似进程,通俗地讲,线程是运行在进程中的“小进程”。

    多线程 

            如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”。多个线程交替占用CPU资源,而非真正的并行执行

    多线程好处

            充分利用CPU的资源

            简化编程模型

            带来良好的用户体验

    主线程(main线程)

            每个Java应用程序都有一个缺省的主线程。我们已经知道,Java 应用程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程称为“主线程”(main线程),该线程负责执行main方法。那么,在main方法的执行中再创建的线程,就称为程序中的其他线程。

            如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束我们的Java 应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句(主线程结束),JVM也不会结束Java应用程序,JVM一直要等到Java应用程序中的所有线程都结束之后,才结束Java应用程序

    线程的创建和启动

    在Java中创建线程的两种方式

            继承java.lang.Thread类

            实现java.lang.Runnable接口

    使用线程的步骤

    1.定义线程

    2.创建线程对象

    3.启动线程

    4.终止线程

    继承Thread类创建线程

            定义MyThread类继承Thread类

            重写run()方法,编写线程执行体

            创建线程对象,调用start()方法启动线程

    多个线程交替执行,不是真正的“并行” ;线程每次执行时长由分配的CPU时间片长度决定

    1. package demo04;
    2. //编写线程类的第一步:创建MyThread类,继承Thread类
    3. public class MyThread extends Thread{
    4. //第二步:重写Thread类的中的run();
    5. @Override
    6. public void run() {
    7. //第三步:在run()方法中写你要运行的代码
    8. for (int i = 1; i <=10; i++) {
    9. System.out.println(Thread.currentThread().getName()+":"+i);
    10. }
    11. }
    12. }

     

    1. package demo04;
    2. public class MyThreadTest {
    3. public static void main(String[] args) {
    4. // 创建线程类对象
    5. MyThread mt1 = new MyThread();
    6. MyThread mt2 = new MyThread();
    7. mt1.setName("线程A");
    8. mt2.setName("线程B");
    9. //启动线程要使用start()方法来启动线程,如果通过线程对象直接调用run()方法,那是由主线程来调用的,不是多线程的实现方式
    10. // mt1.run();
    11. mt1.start();
    12. mt2.start();
    13. }
    14. }

    实现Runnable接口创建线程

            定义MyRunnable类实现Runnable接口

            实现run()方法,编写线程执行体

            创建线程对象,调用start()方法启动线程

     

    1. package demo05;
    2. //第一步:创建线程类实现Runnable类接口
    3. public class MyRunnable implements Runnable{
    4. //第二步:重写run()方法
    5. @Override
    6. public void run() {
    7. //第三步:在run()方法中定义要执行的代码
    8. for (int i = 1; i <=10; i++) {
    9. System.out.println(Thread.currentThread().getName()+":"+i);
    10. }
    11. }
    12. }
    1. package demo05;
    2. public class MyRunnableTest {
    3. public static void main(String[] args) {
    4. // 创建线程类对象
    5. MyRunnable mr = new MyRunnable();
    6. //MyRunnable类中没有start()方法,其父类Object类中也没有start()方法,其实现的接口Runnable中只有run()方法,没有start()方法
    7. //mr.start();
    8. Thread t1 = new Thread(mr,"排山倒海");
    9. Thread t2 = new Thread(mr,"降龙十八掌");
    10. t1.start();
    11. t2.start();
    12. }
    13. }

     比较两种创建线程的方式

            继承Thread类 编写简单,可直接操作线程

            适用于单继承

    实现Runnable接口

            避免单继承局限性

            便于共享资源 

     推荐使用实现Runnable接口方式创建线程

    线程的状态 

    线程优先级 

    线程优先级由1~10表示,1最低,默认优先级为5

    优先级高的线程获得CPU资源的概率较大,不能保证每一次优先级高的线程先获取CPU资源

     

    线程调度 

    线程调度指按照特定机制为多个线程分配CPU的使用权

    方       法

     说       明

    void setPriority(int  newPriority)

    更改线程的优先级

    static void sleep(long millis)

    在指定的毫秒数内让当前正在执行的线程休眠

    void join()

    等待该线程终止

    static void yield()

    暂停当前正在执行的线程对象,并执行其他线程

    void interrupt()

    中断线程

    boolean isAlive()

    测试线程是否处于活动状态

    线程休眠

    让线程暂时睡眠指定时长,线程进入阻塞状态 睡眠时间过后线程会再进入可运行状态

    1. package demo02;
    2. public class MyRunnable implements Runnable {
    3. @Override
    4. public void run() {
    5. for (int i = 1; i <=10; i++) {
    6. System.out.println(Thread.currentThread().getName()+":"+i);
    7. if(i==5){
    8. try {
    9. Thread.sleep(5000);
    10. } catch (InterruptedException e) {
    11. e.printStackTrace();
    12. }
    13. }
    14. }
    15. }
    16. }
    1. package demo02;
    2. //通过线程睡眠来调度线程
    3. public class MyRunnableTest {
    4. public static void main(String[] args) {
    5. MyRunnable mr = new MyRunnable();
    6. Thread t = new Thread(mr);
    7. t.start();
    8. }
    9. }

     线程的强制运行

    使当前线程暂停执行,等待其他线程结束后再继续执行本线程

    public final void join()

    public final void join(long mills)

    public final void join(long mills,int nanos)

    millis:以毫秒为单位的等待时长 nanos:要等待的附加纳秒时长

    需处理InterruptedException异常 

    1. package demo03;
    2. public class MyRunnable implements Runnable {
    3. @Override
    4. public void run() {
    5. for (int i = 1; i <=100; i++) {
    6. System.out.println(Thread.currentThread().getName()+":"+i);
    7. }
    8. }
    9. }
    1. package demo03;
    2. //线程调度之强制执行
    3. public class MyRunnableTest {
    4. public static void main(String[] args) {
    5. Thread t1 = new Thread(new MyRunnable(), "线程A");
    6. t1.start();
    7. for (int i = 1; i <=10; i++) {
    8. if(i==3){
    9. try {
    10. // 线程强制执行:t1强制执行,一直等到t1执行完毕之后,才会释放CPU资源给其它线程执行,在调用join()方法之前,多个线程依然是抢占CPU
    11. t1.join();
    12. } catch (InterruptedException e) {
    13. e.printStackTrace();
    14. }
    15. }
    16. System.out.println(Thread.currentThread().getName()+":"+i);
    17. }
    18. }
    19. }

    线程的礼让

    暂停当前线程,允许其他具有相同优先级的线程获得运行机会 ;该线程处于就绪状态,不转为阻塞状态

    只是提供一种可能,但是不能保证一定会实现礼让

    public static void yield() 

     

    1. package demo04;
    2. public class MyRunnable implements Runnable {
    3. @Override
    4. public void run() {
    5. for (int i = 1; i <=10; i++) {
    6. System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
    7. if(i==3){
    8. System.out.print("线程礼让:");
    9. // 线程礼让:让当前执行run()方法的线程释放CPU资源,给其它线程一个获得CPU资源的机会,但是当前线程还会抢占CPU资源,也就是说,当前线程释放CPU资源后,会与其它线程再一次抢占CPU,就看其它线程能不能抓住这个机会
    10. Thread.yield();
    11. }
    12. }
    13. }
    14. }
    1. package demo04;
    2. //线程调度之线程礼让
    3. public class MyRunnableTest {
    4. public static void main(String[] args) {
    5. MyRunnable mr = new MyRunnable();
    6. Thread t1 = new Thread(mr, "线程A");
    7. Thread t2 = new Thread(mr, "线程B");
    8. t1.start();
    9. t2.start();
    10. }
    11. }

    同步方法

            将线程要操作的代码放入同步方法中,当线程执行到同步方法的时候,一定会执行完同步方法里的所有代码后再释放CPU资源,从而一个线程对数据的操作不会受其它线程的影响。

    使用synchronized修饰的方法控制对类成员变量的访问

    访问修饰符 synchronized 返回类型 方法名(参数列表){……}或者

    synchronized 访问修饰符 返回类型 方法名(参数列表){……} 

    1. package demo06;
    2. public class Site implements Runnable {
    3. // 定义属性表示票库里的票的数量
    4. private int count = 10;
    5. // 定义属性表示用户买到的是第几张票
    6. private int num = 0;
    7. @Override
    8. public void run() {
    9. while (true) {
    10. if(!sale()){
    11. break;
    12. }
    13. }
    14. }
    15. public synchronized boolean sale() {
    16. // 如果票的数量小于0,就不在卖票
    17. if (count <= 0) {
    18. return false;
    19. }
    20. // 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
    21. count--;
    22. num++;
    23. // 买票过程中模拟网速缓慢
    24. try {
    25. Thread.sleep(1000);
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. System.out.println(Thread.currentThread().getName() + "买到了第" + num
    30. + "张票,还剩余" + count + "张票");
    31. return true;
    32. }
    33. }

     

    同步代码块

    使用synchronized关键字修饰的代码块 

    synchronized(syncObject){    

    //需要同步的代码

    }

    syncObject为需同步的对象,通常为this 效果与同步方法相同 

    多个并发线程访问同一资源的同步代码块时

    同一时刻只能有一个线程进入synchronized(this)同步代码块

    当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定

    当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

    1. package demo07;
    2. public class Site implements Runnable {
    3. // 定义属性表示票库里的票的数量
    4. private int count = 10;
    5. // 定义属性表示用户买到的是第几张票
    6. private int num = 0;
    7. @Override
    8. public void run() {
    9. while (true) {
    10. synchronized(this){
    11. // 如果票的数量小于0,就不在卖票
    12. if (count <= 0) {
    13. break;
    14. }
    15. // 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
    16. count--;
    17. num++;
    18. // 买票过程中模拟网速缓慢
    19. try {
    20. Thread.sleep(1000);
    21. } catch (InterruptedException e) {
    22. e.printStackTrace();
    23. }
    24. System.out.println(Thread.currentThread().getName() + "买到了第" + num
    25. + "张票,还剩余" + count + "张票");
    26. }
    27. }
    28. }
    29. }
    1. package demo07;
    2. //同步代码块
    3. public class Test {
    4. public static void main(String[] args) {
    5. Site site = new Site();
    6. Thread t1 = new Thread(site, "张三");
    7. Thread t2 = new Thread(site, "李四");
    8. Thread t3 = new Thread(site, "王五");
    9. t1.start();
    10. t2.start();
    11. t3.start();
    12. }
    13. }

  • 相关阅读:
    flutter系列之:widgets,构成flutter的基石
    关于K8s中资源服务质量管理Resource Qos的一些笔记整理
    HTML基础
    php YII2空数组插入报错问题处理 Array to string conversion
    一、Flume使用
    Red Giant Trapcode Suite 2024.0.1
    Oracle is和as 关键字学习
    【Netty专题】用Netty手写一个远程长连接通信框架
    【Azure 存储服务】访问Azure Blob File遇见400-Condition Headers not support错误的解决之路
    行业解密:为什么跨境电商行业都在做社交媒体营销?
  • 原文地址:https://blog.csdn.net/qq_51810428/article/details/126491403