首页 > 编程语言 >Java 多线程编程

Java 多线程编程

时间:2023-02-10 20:57:21浏览次数:33  
标签:状态 Java Thread 编程 线程 ticket 多线程 public

Java 多线程编程

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。


一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

img

  • 新建状态:

    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:

    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。


线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。


创建一个线程

Java 提供了三种创建线程的方法:

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。
  • 使用线程池例如用Executor框架


synchronized同步锁的使用:

经典案例:售卖火车票

//火车站卖票
public class TicketTask implements Runnable{
    private int ticket = 50;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "说:售出第" + ticket + "张票!");
            ticket --;//卖了一张表
            //间隔2秒钟,卖一张票!
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }//while结束
        if(ticket == 0) {
            System.out.println("售票结束!");
        }
    }
}

测试代码:

public class Test01 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Runnable task = new TicketTask();
        //3个卖票窗口
        new Thread(task,"窗口1").start();//线程1
        new Thread(task,"窗口2").start();//线程2
        new Thread(task,"窗口3").start();//线程3
    }
}

执行效果:

image-20230203151128526

解决问题:

//火车站卖票
public class TicketTask implements Runnable {
    private int ticket = 50;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (ticket > 0) {
            synchronized (this) {
                // 先判断票是否充足
                if (ticket == 0) {
                    System.out.println("售票结束!");
                    return;
                }
                System.out.println(Thread.currentThread().getName() + "说:售出第" + ticket + "张票!");
                ticket--;// 卖了一张表
                // 间隔2秒钟,卖一张票!
                try {
                    Thread.sleep(500);// 单位:毫秒
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } // synchronized结束
        } // while结束
    }
}

线程安全售票的第2种写法:

//火车站卖票
public class TicketTask implements Runnable {
    private int ticket = 50;
    // 将下面的整个都锁起来
    public synchronized void sell() {
        // 先判断票是否充足
        if (ticket == 0) {
            System.out.println("售票结束!");
            return;
        }
        System.out.println(Thread.currentThread().getName() + "说:售出第" + ticket + "张票!");
        ticket--;// 卖了一张表
        // 间隔2秒钟,卖一张票!
        try {
            Thread.sleep(500);// 单位:毫秒
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (ticket > 0) {
            sell();
        }
    }
}

注意:

1.线程间的通信共享数据一定要有同步代码块synchronized

2.一定要有wait和notify,而且二者一定是成对出现。

3.生产者和消费者的线程实现一定是在while(true)里面

标签:状态,Java,Thread,编程,线程,ticket,多线程,public
From: https://www.cnblogs.com/amor2818/p/17110253.html

相关文章

  • JAVA - IO流
    JavaIO流学习总结Java流操作有关的类或接口:Java流类图结构:流的概念和作用流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传......
  • Java - 面向对象 - 多态
    多态多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各......
  • Java基础知识(关系运算符/比较运算符、逻辑运算符)
    一:关系运算符符号说明==a==b,判断a和b的值是否相等,成立为true,不成立为false。!=a!=b,判断a和b的值是否相等,成立为true,不成立为false。>a>b,判断a是否大于b,成立为true,不成立f......
  • 学习打卡01- java入门
    1,基础知识点:java的三个版本javaSE(java基础版),javaEE(java企业版),javaME(小型嵌入式开发)LTS(Longterm<时期>support<支持>)长期支持版公司长期维护包括5.......
  • Typora软件的使用、编程与编程语言、计算机基础、五大组成部分、三大核心硬件、操作系
    目录一、Typora软件的下载与使用(1)、软件下载(2)、markdown语法二、编程与编程语言(1)、什么是语言(2)、什么是编程(3)、什么是编程语言三、计算机本质四、计算机五大组成......
  • JavaScript迭代器与生成器
    JavaScript的迭代器与生成器前沿:可迭代对象及其相关的迭代器是是ES6的一个特性。数组是可迭代的,字符串、set对象和map对象也是。这意味着这些数据结构的内容可以通过......
  • SpringMVC.三 RESTFul编程风格
    1、RESTFuli简介REST:RepresentationalStateTransfer,.表现层资源状态转移,资源资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一......
  • 浏览器中的JavaScript(3)
    3.操作CSS摘要:我们已经知道了JavaScript可以控制HTML文档的逻辑结构和内容。通过对CSS编程,Javascript也可以控制文档的外观和布局。接下来讲解几种JavaScript可以用来操作......
  • java防止频繁请求、重复提交(防抖动)
    在客户端网络慢或者服务器响应慢时,用户有时是会频繁刷新页面或重复提交表单的,这样是会给服务器造成不小的负担的,同时在添加数据时有可能造成不必要的麻烦。自定义注解/......
  • 批处理脚本教程_编程入门自学教程_菜鸟教程-免费教程分享
    教程简介批处理脚本语法-从简单和简单的步骤学习批处理脚本,从基本到高级概念,包括概述,环境,命令,文件,语法,变量,注释,字符串,数组,决策,操作符,日期和时间,输入/输出,返回代码,函数,进......