Java(八)----多线程

news2024/11/27 0:40:02

1. 线程的基本概念

1.1 进程

任何的软件存储在磁盘(硬盘)中,运行软件的时候,OS(操作系统)使用IO技术,将磁盘中的软件的文件加载到内存,程序才能运行。  (进程是从硬盘到内存)

进程的概念 : 应用程序(typerpa,word,IDEA)运行的时候进入到内存,程序在内存中占用的内存空间(进程).

1.2 线程

线程(Thread) : 在内存和CPU之间,建立一条连接通路,CPU可以到内存中取出数据进行计算,这个连接的通路,就是线程.     

一个内存资源 : 一个独立的进程,进程中可以开启多个线程 (多条通路)

并发: 同一个时刻多个线程同时操作了同一个数据

并行: 同一个时刻多个线程同时执行不同的程序

2. Java实现线程程序

今天之前的所有程序都有一个共性 : main启动之后,一条线走到底 (单线程)

2.1 java.lang.Thread类  玩线程用这个类

一切都是对象,线程也是对象,Thread类是线程对象的描述类

  • 实现线程程序的步骤 :

    • 定义类继承Thread

    • 子类重写方法run

    • 创建子类对象

    • 调用子类对象的方法start()启动线程

//- 定义类继承Thread
//- 子类重写方法run
public class SubThread extends Thread {
    public void run(){
        for(int x = 0 ; x < 50 ;x++)
            System.out.println("run..."+x);
    }
}
public static void main(String[] args) {
    //创建线程程序
    SubThread subThread = new SubThread();
    //调用子类对象的方法start()启动线程
    //启动线程,JVM调用方法run
    subThread.start();
    for(int x = 0 ; x < 50 ;x++)
    	System.out.println("main..."+x);
}

此处重写了run()方法,但没有直接调用run()方法,这种方法被称为----------回调函数

2.2 线程的内存图

2.3 Thread类方法

  • Thread类的方法 getName()返回线程的名字,返回值是String类型

public class ThreadName extends Thread {
    public void run (){
        System.out.println("线程名字:: "+ super.getName());
    }
}
   public static void main(String[] args) {
        ThreadName threadName = new ThreadName();
        //threadName.setName("旺财");
        threadName.start();

        ThreadName threadName1 = new ThreadName();
        //threadName1.setName("小强");
        threadName1.start();
    }
  • Thread类静态方法 : Thread  currentThread().getName()

    • 静态调用,作用是返回当前的线程对象

    • "当前" 线程,( 当今皇上. 本地主机(谁运行谁就是本地主机) )

//获取当前线程对象,拿到运行main方法的线程对象
Thread thread =  Thread.currentThread();
System.out.println("name::"+thread.getName());
  • Thread类的方法 join()

    • 解释,执行join()方法的线程,他不结束,其它线程运行不了

    public static void main(String[] args) throws InterruptedException {
        JoinThread t0 = new JoinThread();
        JoinThread t1 = new JoinThread();

        t0.start();
        t0.join();
        t1.start();
    }
  • Thread类的方法 static yield()

    • 线程让步,线程把执行权让出

    public void run() {
        for(int x = 0 ; x < 50 ;x++){
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"x.."+x);
        }
    }

3. Java实现线程程序

3.1 java.lang.Runnable接口

  • 实现线程程序的步骤 :

    • 定义类实现接口

    • 重写接口的抽象方法run()

    • 创建Thread类对象

      • Thread类构造方法中,传递Runnable接口的实现类对象

    • 调用Thread对象方法start()启动线程

//- 定义类实现接口
// - 重写接口的抽象方法run()
public class SubRunnable implements Runnable{
    @Override
    public void run() {
        for(int x = 0 ; x < 50 ;x++){
            System.out.println(Thread.currentThread().getName()+"x.."+x);
        }
    }
}
    public static void main(String[] args) {
        //创建接口实现类对象
        Runnable r = new SubRunnable();
        //创建Thread对象,构造方法传递接口实现类
        Thread t0 = new Thread(r);
        t0.start();

        for(int x = 0 ; x < 50 ;x++){
            System.out.println(Thread.currentThread().getName()+"x.."+x);
        }
    }

3.2 实现接口的好处

接口实现好处是设计上的分离效果 : 线程要执行的任务和线程对象本身是分离的.

继承Thread重写方法run() : Thread是线程对象,run()是线程要执行的任务 

实现Runnable接口 : 方法run在实现类,和线程无关,创建Thread类传递接口的实现类对象,线程的任务和Thread没有联系, 解耦合

4. 线程安全

出现线程安全的问题需要一个前提 : 多个线程同时操作同一个资源

线程执行调用方法run,同一个资源是堆内存的

4.1 售票例子

火车票的票源是固定的,购买渠道在火车站买,n多个窗口

/**
 * 票源对象,需要多个线程同时操作
 */
public class Ticket implements Runnable {

    //定义票源
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(10);//线程休眠,暂停执行
                }catch (Exception ex){}
                System.out.println(Thread.currentThread().getName()+" 出售第" + tickets + "张");
                tickets--;
            }else
                break;;
        }
    }
}
public static void main(String[] args) {
    Ticket ticket = new Ticket();
    //创建3个窗口,3个线程
    Thread t0 = new Thread(ticket);
    Thread t1 = new Thread(ticket);
    Thread t2 = new Thread(ticket);

    t0.start();
    t1.start();
    t2.start();
}

线程的安全问题:多线程操作一个资源,就有可能会出现线程的安全问题

出现的原因:当一个线程还没有操作完成,另一个线程就开始操作了

解决线程的安全问题 : 当一个线程没有完成全部操作的时候,其它线程不能操作

4.2 同步代码块

同步代码块可以解决线程安全问题 : 格式 synchronized关键字

synchronized(任意对象){
    //线程操作的共享资源
}

任意对象 : 在同步中这个对象称为对象锁,简称,官方的稳定称为 对象监视器

同步代码块,如何保证线程的安全性. 

  • 同步代码块的执行原理 : 关键点就是对象锁

    • 线程执行到同步,判断锁是否存在

      • 如果锁存在,获取到锁,进入到同步中执行

      • 执行完毕,线程出去同步代码块,将锁对象归还

    • 线程执行到同步,判断锁所否存在

      • 如果锁不存在,线程只能在同步代码块这里等待,锁的到来

使用同步 : 线程要先判断锁,然后获取锁,出去同步要释放锁, 增加了许多步骤,因此线程安全运行速度慢. 牺牲性能,不能牺牲数据安全 

4.3 同步方法

当一个方法中,所有代码都是线程操作的共享内容,可以在方法的定义上添加同步的关键字 synchronized , 同步的方法,或者称为同步的函数.

  • 同步方法中有对象锁吗 , this对象

  • 静态同步方法中有对象锁吗,锁对象是本类.class属性. 这个属性表示这个类的class文件的对象.

    @Override
    public void run() {
        while (true)
          sale();
    }

private static synchronized void sale(){
    //  synchronized (Ticket.class) {
    if (tickets > 0) {
    try {
        Thread.sleep(20);//线程休眠,暂停执行
        } catch (Exception ex) {
    }
    System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
    tickets--;
    }
//  }
}

 类名.class    拿去class文件的对象    (栈内存、堆内存、元数据区)

5. 死锁 (面试考点)

死锁程序 : 多个线程同时争夺同一个锁资源,出现程序的假死现象

面试点 : 考察开发人员是否充分理解同步代码的执行原理

同步代码块 : 线程判断锁,获取锁,释放锁,不出代码,锁不释放

完成死锁的案例 : 同步代码块的嵌套

public class ThreadDeadLock implements Runnable {

    private boolean flag;

    public ThreadDeadLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        while (true) {
            //同步代码块的嵌套
            if (flag) {
                //先进入A锁同步
                synchronized (LockA.lockA) {
                    System.out.println("线程获取A锁" + Thread.currentThread().getName());
                    //在进入另一个同步B锁
                    synchronized (LockB.lockB) {
                        System.out.println("线程获取B锁" + Thread.currentThread().getName());
                    }
                }
            } else {
                //先进入b锁再进入a锁
                synchronized (LockB.lockB) {
                    System.out.println("线程获取B锁" + Thread.currentThread().getName());
                    synchronized (LockA.lockA) {
                        System.out.println("线程获取A锁" + Thread.currentThread().getName());
                    }
                }
            }
        }
    }
}
public class LockB {
    public static LockB lockB = new LockB();
}
public class LockA {
    public static LockA lockA = new LockA();
}
public class ThreadTest {
    public static void main(String[] args) {
        ThreadDeadLock threadDeadLock1 = new ThreadDeadLock(true);
        ThreadDeadLock threadDeadLock2 = new ThreadDeadLock(false);

        new Thread(threadDeadLock1).start();
        new Thread(threadDeadLock2).start();
    }
}

6. JDK5新特性Lock锁

JDK5新的特性 : java.util.concurrent.locks包. 定义了接口Lock.

Lock接口替代了synchronized,可以更加灵活  6以后优化了synchronized

  • Lock接口的方法

    • void lock() 获取锁

    • void unlock()释放锁

  • Lock接口的实现类ReentrantLock

/**
 *  优化为juc包的接口Lock
 */
public class Ticket implements Runnable {

    //定义票源
    private  int tickets = 100;
    //获取Lock接口的实现类对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true)
          sale();
    }

    private void sale(){
        //获取锁
        lock.lock();
        if (tickets > 0) {
            try {
                Thread.sleep(20);//线程休眠,暂停执行
            } catch (Exception ex) {
            }
            System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
            tickets--;
        }
        //释放锁
        lock.unlock();
    }
}

7. 生产者与消费者

创建2个线程,一个线程表示生产者,另一个线程表示消费者  生产一个消费一个

多线程操作一个资源(但是资源的操作方向是不同的,生产一个消费一个

如何实现线程通信?(wait等待 ,notify唤醒)如何确定生产者生产了flag,如何确定消费者消费了flag,

/**
 * 定义资源对象
 *   成员 : 产生商品的计数器
 *          标志位
 */
public class Resource {
    int count ;
    boolean flag ;
}
/**
 * 生产者线程
 * 资源对象中的变量++
 */
public class Produce implements Runnable {

    private Resource r;

    public Produce(Resource r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true){
            synchronized (r) {
                //是否要消费,判断标志位 ,允许消费才能执行
                if (!r.flag )
                    //消费完成,不能再次消费,等待生产
                    try{r.wait();}catch (Exception ex){}
                System.out.println("消费第" + r.count);
                //消费完成后,修改标志位,变成已经消费
                r.flag = false;
                //唤醒生产线程
                r.notify();
            }
        }
    }
}
/**
 * 消费者线程
 *      资源对象中的变量输出打印
 * */
public class Customer implements Runnable{

    private Resource r;

    public Customer(Resource r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true){
            synchronized (r) {
                //是否要消费,判断标志位 ,允许消费才能执行
                if (!r.flag )
                    //消费完成,不能再次消费,等待生产
                    try{r.wait();}catch (Exception ex){}
                System.out.println("消费第" + r.count);
                //消费完成后,修改标志位,变成已经消费
                r.flag = false;
                //唤醒生产线程
                r.notify();
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Resource resource = new Resource();
        //接口实现类,生产,消费
        Produce produce = new Produce(resource);
        Customer customer = new Customer(resource);
        //创建线程
        new Thread(produce).start();
        new Thread(customer).start();
    }
}
  • 线程通信的方法 wait() notify()

    • 方法的调用必须写在同步中

    • 调用者必须是作为锁的对象

    • wait(),notify()为什么要定义在Object类

      • 同步中的锁,是任意对象,任何类都继承Object

  • 案例改为方法实现

/**
 * 定义资源对象
 *   成员 : 产生商品的计数器
 *          标志位
 */
public class Resource {
   private int count ;
   private boolean flag ;

   //消费者调用
   public synchronized void getCount() {
         //flag是false,消费完成,等待生产
         if (!flag)
            //无限等待
            try{this.wait();}catch (Exception ex){}
         System.out.println("消费第"+count);
            //修改标志位,为消费完成
         flag = false;
         //唤醒对方线程
         this.notify();
   }
   //生产者调用
   public synchronized void setCount() {
         //flag是true,生产完成,等待消费
         if (flag)
            //无限等待
            try{this.wait();}catch (Exception ex){}
         count++;
         System.out.println("生产第"+count+"个");
         //修改标志位,为生产完成
         flag = true;
         //唤醒对方线程
         this.notify();
   }
}
/**
 * 消费者线程
 *   资源对象中的变量输出打印
 */
public class Customer implements Runnable{
    private Resource r ;

    public Customer(Resource r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.getCount();
        }
    }
}
/**
 * 生产者线程
 *   资源对象中的变量++
 */
public class Produce implements Runnable{

    private Resource r ;

    public Produce(Resource r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.setCount();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/27332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Marked.js让您的文档编辑更加轻松自如!

低代码应用平台——kintone既可以保留更改记录&#xff0c;也有流程管理的功能&#xff0c;在公司内部分享会议记录啊、wiki等文档或学习资料等时非常的便利。 kintone还有丰富的文本编辑框&#xff0c;可以对内容进行编辑提高易读性。但是还是有不少人觉得如果能够使用Markdo…

19.[Python GUI] PyQt5中的模型与视图框架-基本原理

PyQt中的模型与视图框架 一、Qt中模型与视图相关的类 二、模型与视图的基本原理 MVC把图形界面分为三个部分&#xff1a;模型&#xff08;Model&#xff09;&#xff0c;视图&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09;&#xff0c; 模型&#x…

Git大型文件存储

什么是 Git LFS&#xff1f; Git 是跟踪代码库演变和与同行高效协作的绝佳选择。但是&#xff0c;当您要跟踪的存储库非常大时会发生什么&#xff1f; 如果您考虑一下&#xff0c;存储库变得庞大的主要原因大致有两个&#xff1a; 他们积累了非常非常长的历史&#xff08;项目…

【C】文件操作fopen与fclose

目录 函数 1.fopen 2.fclose 3.freopen 函数 头文件 #include<stdio.h> 1.fopen FILE *fopen(const char *restrict dilename,const char* restrict mode); 作用&#xff1a;打开文件参数&#xff1a; 第一个是含有要打开文件名的字符串&#xff08;"文件名…

肝了一周的八万字Redis实战篇

Redis实战篇 文章目录Redis实战篇开篇导读1. 短信登录2. 商户查询缓存3. 优惠卷秒杀4. 附近的商户5. UV统计6. 用户签到7. 好友关注8. 达人探店一、短信登录1. 导入黑马点评项目1.1 导入SQL1.2 有关当前模型1.3 导入后端项目1.4 导入前端工程1.5 运行前端项目2. 基于Session实现…

【杂谈】快来看看如何使用LGMT这样的蜜汁缩写来进行CodeReview吧!

文章目录一、先从一个梗开始说起吧&#xff01;二、什么是LGTM&#xff1f;2.1 LGTM 是什么意思&#xff1f;2.2 蹭梗品牌故事2.3 虚假的CodeReview三、Code Review中的蜜汁缩写四、参考链接一、先从一个梗开始说起吧&#xff01; 公司最近在如火如荼的开展CodeReview活动&…

Reinforcement learning from demonstration through shaping(Wiewiora 2003)

摘要 强化学习中的一个重要问题是如何以有原则的方式整合专家知识&#xff0c;尤其是当我们扩展到现实世界的任务时。在本文中&#xff0c;我们提出了一种在不改变最优策略的情况下将任意建议纳入强化学习agent的奖励结构的方法。 该方法将 Ng 等人 (1999) 提出的基于势能的塑…

1530_AURIX_TriCore内核架构_通用寄存器以及系统寄存器

全部学习汇总&#xff1a; GreyZhang/g_tricore_architecture: some learning note about tricore architecture. (github.com) 继续看一下内核手册&#xff0c;这次了解一下通用寄存器以及系统寄存器。最近一段时间最复位以及trap困扰了许久&#xff0c;看看这里面是否能够获取…

黄河水稻山东智慧 国稻种芯·中国水稻节:济南泉城米袋子

黄河水稻山东智慧 国稻种芯中国水稻节&#xff1a;济南泉城米袋子 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道&#xff1a;又是一年春天。济南黄河流域吴家堡水稻田旁的…

数据挖掘与机器学习:数据挖掘算法原理与实践:数据预处理

目录 第一关&#xff1a;标准化 任务描述&#xff1a; 相关知识&#xff1a; 一、为什么要进行标准化 二、Z-score标准化 三、Min-max标准化 四、MaxAbs标准化 编程要求&#xff1a; 测试说明&#xff1a; 第二关&#xff1a;非线性转换 任务描述&#xff1a; 相关知…

【LeetCode】878. 第 N 个神奇数字

题目描述 一个正整数如果能被 a 或 b 整除&#xff0c;那么它是神奇的。 给定三个整数 n , a , b &#xff0c;返回第 n 个神奇的数字。因为答案可能很大&#xff0c;所以返回答案 对 109 7 取模 后的值。 示例 1&#xff1a; 输入&#xff1a;n 1, a 2, b 3 输出&#xff…

stm32cubemx hal学习记录:FreeRTOS互斥量

一、互斥量 1、互斥量用于互锁&#xff0c;可以充当资源保护的令牌&#xff0c;当一个任务希望访问某个资源时&#xff0c;它必须先获取令牌&#xff0c;当任务使用完资源后&#xff0c;必须返还令牌&#xff0c;以便其他任务可以访问该资源。 2、互斥量一般用于临界资源保护…

[附源码]计算机毕业设计JAVA教师档案管理系统

[附源码]计算机毕业设计JAVA教师档案管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

LabVIEW通信-CAN

文章目录CANcan总线特点位定位与同步标识符检验滤波报文传输类型CAN CAN属于OSI的物理层与数据链路层 can总线特点 网络各节点之间的数据通信实时性强 开发周期短 结构简单&#xff08;只有两根线与外部相连&#xff0c;内部继承了错误探测和管理模块&#xff09; 数据通信没…

Spring IOC

一、为什么要使用Spring&#xff1f; Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用&#xff0c;但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发&#xff0c;并通过POJO为基础的编程模型促进良好的编程习惯。 为…

RPA案例|云扩助力保险行业开启超自动化运营新阶段

近五年&#xff0c;全球平均保险深度总体呈小幅平稳下降趋势&#xff0c;2021年中国保险深度为4.15%&#xff0c;全球平均保险深度为5.96%&#xff0c;而美国、英国等发达国家的保险深度则保持在10%以上&#xff0c;中国保险深度仍然具有很大的上升空间。 为进一步拓展增量空间…

软考是什么?---2023年软考最全解析

软考是什么&#xff1f; 软考全称为&#xff1a;计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;发证机构为国家人力资源和社会保障部、工业和信息化部&#xff0c;简称为&#xff1a;人社部和工信部。中日韩三国互认&#xff0c;证书的含金量很高…

Navicat操作数据库与Mysql常见命令操作实战

一&#xff1a;Navicat下载与安装 官网下载链接&#xff1a;Navicat 下载完后直接安装即可 二&#xff1a;数据库的连接 1.打开Navicat软件&#xff0c;点击左上角连接按钮&#xff0c;选择mysql数据库 输入完成后双击连接名&#xff0c;连接成功后&#xff0c;小海豚变绿色 …

Advances in Graph Neural Networks笔记5:Dynamic Graph Neural Networks

诸神缄默不语-个人CSDN博文目录 本书网址&#xff1a;https://link.springer.com/book/10.1007/978-3-031-16174-2 本文是本书第五章的笔记。 懒得看了&#xff01;反正我也不是做这个的。以后要是真的去做动态图了也不是没资源找&#xff0c;这个我懒得看了&#xff0c;就写一…

CentOS 7安装Redis+Springboot整合Redis

1、虚拟环境条件&#xff1a; 已安装VMwareVMware Tools 使用的是视图界面并配置了基本开发环境&#xff0c;参照超详细VMware安装CentOs图文教程_Sq夏颜的博客-CSDN博客 2、直接把已经下载好的redis安装包拖进来&#xff0c;下载地址&#xff1a;CRUG网站 3、解压 tar -zxv…