多线程进阶篇----常用方法

news2024/11/18 23:50:50

文章目录

  • 线程状态
  • 线程方法
    • 线程礼让
    • 线程优先级
    • 守护线程
  • 线程同步
  • 生产者消费者问题
    • 解决方式一:管程法
    • 方法二:标志位法
  • 死锁
  • 总结

线程状态

线程有5种状态,新生态、就绪态、阻塞态、运行态、死亡态
在这里插入图片描述
在该图上,就绪状态和运行状态是一个双向箭头,这里是双向箭头的原因是是在线程中有一个线程礼让,当线程进行线程礼让之后,线程会从运行状态转为就绪状态。所以在上面的图上的就绪状态到运行状态是一个双向箭头。

这里需要注意的是当线程被new出来之后并不一定会立即执行,而是进入线程的就绪状态,等待CPU的调度,当线程进入运行状态之后,线程才真正的执行县城提的代码块。

线程方法

线程礼让

礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功!看CPU心情

public class ThreadYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }

}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName()+"停止执行");
    }
}

线程优先级

在线程中,线程获取CPU的调用有多种方式,但是在java中线程获取CPU调用是采用的抢占式进行的。也就是多个线程在抢夺CPU的资源,CPU的什么时候执行哪个线程是不确定的,执行多长时间也是不确定的,所以抢占式调度体现了一个随机性
如果不设置线程的优先级那么默认是5。

public class TestPriority {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority testPriority=new MyPriority();
        Thread thread1=new Thread(testPriority,"线程1");
        Thread thread2=new Thread(testPriority,"线程2");
        Thread thread3=new Thread(testPriority,"线程3");
        Thread thread4=new Thread(testPriority,"线程4");

        //先设置优先级再启动
        thread1.start();

        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(4);
        thread3.start();

        thread4.setPriority(Thread.MAX_PRIORITY);
        thread4.start();
    }
}

class  MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

守护线程

线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
应用场景:后台记录操作日志,监控内存,垃圾回收

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认是false,表示用户线程

        thread.start();
        new Thread(you).start();
    }
}

class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑着你");
        }
    }
}

class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3600; i++) {
            System.out.println("你一生平安");
        }
        System.out.println("goodBye");
    }
}

线程同步

场景:有三个人(我、你、黄牛)同时抢票,每个人都想抢到票

先看代码:

public class TestThread4 implements Runnable{
    //票数
    private int ticketNums=10;
    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            //模拟延时
            try{
                Thread.sleep(200);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票");
        }
    }

    public static void main(String[] args) {
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛").start();
    }
}



根据上面的代码可能会出现两个人或多跟人拿到同一张票:
在这里插入图片描述
那如何解决这个问题呢?由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来方问
冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待
使用后释放锁即可。
同步后的代码:

public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"我").start();
        new Thread(station,"ni").start();
        new Thread(station,"黄牛").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNums=10;//票

    boolean flag=true;//外部停止方法


    @Override
    public void run() {
       while (flag){
           try {
               buy();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }

    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if(ticketNums<=0){
            flag=false;
            return;
        }

        //模拟延时
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}

存在以下问题:
◆一个线程持有锁会导致其他所有需要此锁的线程挂起;
◆在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引
起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒
置,引起性能问题。

生产者消费者问题

应用场景:生产者和消费者问题
◆假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将
仓库中产品取走消费
◆如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到
仓库中的产品被消费者取走为止
◆如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,
直到仓库中再次放入产品为止

在这里插入图片描述

解决方式一:管程法

生产者:负责生产数据的模块(可能是方法,对象,线程,进程):
◆消费者:负责处理数据的模块(可能是方法,对象,线程,进程):
◆缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区“
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

下面用一个买卖东西的例子来说一下,有一个仓库,当仓库中没有产品之后,卖家就往仓库中添加产品,当买家看到仓库中有产品之后就去消费。买家消费的时候卖家等待,卖家生产的时候买家等待。

代码示例:
生产者:

//生产者
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container){
        this.container=container;
    }

    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                container.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生产了"+i+"只鸡");
        }
    }
}


消费者:

//消费者
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费了-->"+container.pop().id+"只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


产品:

//产品
class Chicken{
    int id;//产品编号

    public Chicken(int id) {
        this.id = id;
    }
}

```java
缓冲区

//缓冲区
class SynContainer{
    //容器大小
    Chicken[] chickens=new Chicken[10];

    // 容器计数器
    int count=0;

    //生产者放入产品
    public  synchronized void push(Chicken chicken) throws InterruptedException {
        //如果容器满了,需要等待消费者消费
            if(count== chickens.length){
                //通知消费者消费,生产者等待
                this.wait();
            }
        //如果没有满,我们就需要丢入产品
            chickens[count]=chicken;
            count++;
        System.out.println("count------"+count);
            //可以通知消费者消费
            this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop() throws InterruptedException {
        //判断能否消费
        if (count==0){
            //等待生产者生产,消费者等待
            this.wait();
        }

        //如果可以消费
        count--;
        Chicken chicken=chickens[count];
        System.out.println("消费者---"+count);
        //吃完了,通知生产者生产
        return chicken;
    }
}

主函数:

public class TubeSide {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}




方法二:标志位法

类似于信号灯,当处于什么信号的时候,执行什么状态。
方法二:标志位法

下面用一个演员和观众的例子来说一下标志位法,当演员表示的时候,观众等待,演员表演完之后通知观众来观看,这时候演员等待。同理,当观众观看时,演员等待。

生产者:


//生产者---演员
class Player extends Thread{
    TV tv;

    public Player(TV tv) {
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                try {
                    this.tv.play("播放中");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                try {
                    this.tv.play("记录生活");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

消费者

//消费者---观众
class Watcher extends Thread{
    TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                tv.watch();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



产品

//产品---节目
class TV{
    //演员表演,观众等待
    //观众观看,演员等待

    String voice;//表演的节目
    boolean flag=true;

    //表演
    public synchronized void play(String voice) throws InterruptedException {
        if(!flag){
            this.wait();
        }
        System.out.println("演员表演了"+voice);
        //通知观众观看
        this.notify();
        this.voice=voice;
        this.flag=! this.flag;
    }

    //观看
    public synchronized void watch() throws InterruptedException {
        if (flag){
            this.wait();
        }
        System.out.println("观看了:"+voice);

        //通知演员表演
        this.notify();
        this.flag=!this.flag;
    }
}



死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而
导致两个或者多个线程都在等待对方释放资源,都停止执行的情形某一个同步块
同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题

避免死锁的办法:
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件
就可以避免死锁发生

总结

线程是计算机程序中执行的最小单位,它是进程中的一个独立执行路径。多线程编程使得程序可以同时执行多个任务,提高了程序的并发性和响应性。

以下是关于线程的一些总结:

线程的创建:线程可以通过继承Thread类或实现Runnable接口来创建。通过继承Thread类,需要重写run()方法来定义线程的任务;通过实现Runnable接口,需要实现run()方法,并将Runnable对象传递给Thread类的构造函数来创建线程。

线程的状态:线程可以处于多个状态,包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)等。线程的状态会随着线程的执行和调度而不断变化。

线程同步:在多线程环境下,可能会出现多个线程同时访问共享资源的情况,为了避免竞态条件和数据不一致的问题,需要使用线程同步机制。常用的线程同步机制包括使用synchronized关键字、使用ReentrantLock类、使用volatile关键字等。

线程通信:线程之间可以通过共享变量、等待/通知机制、管道、锁等方式进行通信。常用的线程通信方式包括使用wait()、notify()、notifyAll()方法实现等待/通知机制,以及使用BlockingQueue、CountDownLatch、CyclicBarrier等线程同步工具类。

线程池:线程池是一种管理和复用线程的机制,它可以避免频繁创建和销毁线程的开销。通过线程池,可以方便地管理线程的生命周期、控制线程的数量、提高线程的执行效率。

线程安全:线程安全是指多个线程同时访问某个资源时,不会出现数据不一致或不正确的情况。编写线程安全的代码需要考虑对共享资源的并发访问控制,使用合适的同步机制来保证数据的一致性和正确性。

线程调度:线程调度是操作系统或虚拟机决定哪个线程在某个时刻执行的过程。线程调度算法可以根据不同的策略和优先级来决定线程的执行顺序和时间片分配。

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

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

相关文章

webpack联邦模块介绍及在dumi中使用问题整理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ModuleFederationPlugin参数含义&#xff1f;二、如何在dumi中使用及问题整理1. 如何在dumi中使用(这个配置是好使的)2.相关问题整理2.1 问题12.2 问题2 总…

Android 之 动画合集之属性动画 -- 初见

本节引言&#xff1a; 本节给带来的是Android动画中的第三种动画——属性动画(Property Animation)&#xff0c; 记得在上一节Android 之 动画合集之补间动画为Fragment 设置过渡动画的时候&#xff0c;说过&#xff0c;App包和V4包下的Fragment调用setCustomAnimations()对应…

白皮书精彩案例分享 | 数字孪生:让治水用水有了“智慧大脑”

山有百藏而不言&#xff0c;水润万物而不语。中国属于大河文明&#xff0c;农业历来在经济中占主导地位&#xff0c;其中水利灌溉是保证农业生产和提高农业产量的重要因素。 然而&#xff0c;由于过去水利工程建设缺乏预见性&#xff0c;传统水利工程在作出贡献的同时&#xf…

JavaScript 简单实现观察者模式和发布订阅模式

JavaScript 简单实现观察者模式和发布订阅模式 1. 观察者模式1.1 如何理解1.2 代码实现 2. 发布订阅模式2.1 如何理解2.2 代码实现 1. 观察者模式 1.1 如何理解 概念&#xff1a;观察者模式定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff…

重生之我要学C++第三天(类和对象)

我重生了&#xff0c;这篇文章就深入的探讨C中的类和对象。 一.类的引入和定义 类的引入&#xff1a;在C语言中&#xff0c;结构体内部只能定义变量或者结构体&#xff0c;C中对结构体进行了升级->类&#xff0c;C的类中既可以定义变量&#xff0c;又可以定义函数。类中的变…

TSINGSEE青犀视频安防监控视频平台EasyCVR新增密码复杂度提示

智能视频监控平台TSINGSEE青犀视频EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTM…

BERT模型和Big Bird模型对比

BERT模型简介 BERT模型是基于Transformers的双向编码器表示&#xff08;BERT&#xff09;&#xff0c;在所有层中调整左右情境&#xff08;学习上下层语义信息&#xff09;。 Transformer是一种深度学习组件&#xff0c;能够处理并行序列、分析更大规模的数据、加快模型训练速…

sftp和scp协议,哪个传大文件到服务器传输速率快?

环境&#xff1a; 1.Win scp 6.1.1 2.XFTP 7 3.9.6G压缩文件 4.Centos 7 5.联想E14笔记本Win10 6.HW-S1730S-S48T4S-A交换机 问题描述&#xff1a; sftp和scp协议&#xff0c;哪个传大文件到服务器速度快&#xff1f; 1.SFTP 基于SSH加密传输文件&#xff0c;可靠性高&am…

Profinet转EtherNet/IP网关连接AB PLC的应用案例

西门子S7-1500 PLC&#xff08;profinet&#xff09;与AB PLC以太网通讯&#xff08;EtherNet/IP&#xff09;。本文主要介绍捷米特JM-EIP-PN的Profinet转EtherNet/IP网关&#xff0c;连接西门子S7-1500 PLC与AB PLC 通讯的配置过程&#xff0c;供大家参考。 1, 新建工程&…

护网行动:ADSelfService Plus引领企业网络安全新纪元

随着信息技术的飞速发展&#xff0c;企业网络的重要性变得愈发显著。然而&#xff0c;随之而来的网络安全威胁也日益增多&#xff0c;网络黑客和恶意软件不断涌现&#xff0c;给企业的数据和机密信息带来巨大风险。在这个信息安全威胁层出不穷的时代&#xff0c;企业急需一款强…

API攻击原理,以及如何识别和预防

攻击者知道在针对API时如何避开WAF和API网关。以下是一些公司应对API攻击快速增长的示例。 5月初&#xff0c;Pen Test Partners 安全研究员 Jan Masters 发现&#xff0c;他竟然能够在未经身份验证的情况下&#xff0c;向Peloton的官方API提出可获取其它用户私人数据的请求&am…

TEE GP(Global Platform)功能认证产品

TEE之GP(Global Platform)认证汇总 一、功能认证产品介绍 选择Functional和TEE Initial Configuration v1.1&#xff0c;然后SEARCH&#xff0c;可以看到TEE对应的功能认证产品。 二、CK810MFT V3.8, ERAGON V3, ALIBABA CLOUD LINK TEE V1.2.0 参考&#xff1a; GlobalPlatf…

知乎高赞|什么是低代码,强烈推荐!

本文摘自知乎用户吴多益的文章《从实现原理看低代码》&#xff0c;与以往抽象的定义不同&#xff0c;本文是从代码的角度定义低代码&#xff0c;有非常高的学习价值&#xff01;欢迎大家去看原文。 在讨论各个低代码方案前&#xff0c;首先要明确「低代码」究竟是什么&#xff…

微信联系人批量删除功能如何操作?删除的联系人如何恢复?

继微信推出了朋友圈置顶功能后&#xff0c;微信又推出了"批量删除好友的功能" &#xff0c;具体的操作步骤如下&#xff1a; 第一步 是点击聊天界面上的搜索框"搜索" 第二步 "搜索"排序字母&#xff0c;点击"更多联系人" 第三步 搜…

GNN的一篇入门 :A Gentle Introduction to Graph Neural Networks

原文链接 A Gentle Introduction to Graph Neural Networks (distill.pub)https://distill.pub/2021/gnn-intro/ 内容简介&#xff1a;本文是“A Gentle Introduction to Graph Neural Networks”的阅读笔记&#xff0c;因为第一次接触GNN&#xff0c;很多深奥的概念不懂&…

a柱透明屏好处和挑战详解

a柱透明屏是一种新型的汽车技术&#xff0c;它可以将车辆的a柱部分变得透明&#xff0c;提高驾驶员的视野和安全性。这项技术的出现&#xff0c;将为驾驶员提供更好的驾驶体验和更高的安全性能。 a柱是汽车车身结构中的一部分&#xff0c;位于车辆前部&#xff0c;连接车顶和车…

wangEditor初探

1、前言 现有的Quill比较简单&#xff0c;无法满足业务需求&#xff08;例如SEO的图片属性编辑需求&#xff09; Quill已经有比较长的时间没有更新了&#xff0c;虽然很灵活&#xff0c;但是官方demo都没有一个。 业务前期也没有这块的需求&#xff0c;也没有考虑到这块的扩展…

总结 Android 开发中截取字符串的方法

string str”hello word”;int i5; 1 取字符串的前i个字符 strstr.Substring(0,i); // or strstr.Remove(i,str.Length-i);substring(start,end)&#xff1a;substring是截取2个位置之间及start-end之间的字符串2 去掉字符串的前i个字符&#xff1a; strstr.Remove(0,i); // or…

HTTP vs HTTPS: 网络安全的重要转变

文章目录 一、HTTP的缺点1.1 通信使用明文可能会被窃听1.2 不验证通信方的身份就可能遭遇伪装1.3 无法证明报文完整性&#xff0c;可能已遭篡改 二、 HTTP 加密 认证 完整性保护 HTTPS2.1 HTTPS 是身披 SSL 外壳的 HTTP2.2 HTTPS采用混合加密机制2.3 HTTPS存在的问题 一、HTT…

JavaScript --简介

目录 JS可以用来做什么&#xff1f; JS在前端中几种写法: 1. 文件引用&#xff1a; 2. 页面样式 3. 行内样式 集中常见的弹框: JS基本语法&#xff1a; 变量&#xff1a; 常量&#xff1a; 数据类型&#xff1a; 基本数据类型&#xff1a; 引用数据类型&#xff1a…