多线程初阶

手把手配置:用Synopsys VDK为英飞凌TC4x系列MCU做虚拟原型开发 本文详细介绍了如何使用Synopsys VDK为英飞凌TC4x系列MCU进行虚拟原型开发,涵盖开发环境搭建、关键外设虚拟化配置、AUTOSAR栈集成及虚拟调试与性能调优全流程。通过虚拟原型技术,开发者可提前6-12个月启动软件开发,显著提升汽车电子项目的开发效率。 阅读详情

目录

1,多线程的实现

2,多线程相关讨论以及方法

3,线程状态

4,线程安全

5,wait/notify

一,多线程的实现

前情了解:

在代码中实现多线程的前提下,我们需要了解一下多线程的背景,cpu我们都知道,是电脑的大脑,电脑的计算,逻辑判断以及任务的调度都需要他,那为了提高开发的效率以及电脑的效率,我们可以提高cpu的能力,早期的cpu是单核的,也就是只有一个核心,这个时候我们如果要实现程序真正的并行(两个任务同时进行)运行的话,就得用到多核心,多核cpu通过多个核心从而调用多个进程来同时执行任务,但是进程的创建和销毁的成本很高,所以我们引入了更小的单位,也就是我们所说的线程,并且同一时刻,一个 CPU 核心只能执行一个线程。

进程:操作系统分配资源的最小单位

线程:操作系统调度执行的最小单位

例子:cpu就相当于我们干活的员工,进程就是一个工厂,线程就是一套流水线,单核cpu就相当于一个工人,多核就相当于多个工人,那肯定是多核好,可以提高效率,现在我们要提高收益,我们可以多建一些厂,也就是多进程,这个开销肯定很大,所以我们引入更轻量化的线程,在一个工厂内设置多个流水线,实现资源的共享,减小开销同时还可以提高效率

第一种方式:继承Thread类

Thread是系统提供的多线程类,里面继承了Runnable接口,也就相当于Thread是负责去执行的,然后Runnable里面的run方法是我们要执行什么,Runnable也是一个函数接口,因为里面只有一个抽象方法,所以后续我们改进就可以使用lambda表达式,在继承的时候需要重写接口的方法

//继承Thread类
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("继承Thread类实现的多线程");
    }
}
MyThread myThread=new MyThread();
        myThread.start();

第二种方式:实现Runnable接口

实现Runnable接口然后将其作为参数传给Thread

//实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口实现的多线程");
    }
}
MyRunnable myRunnable=new MyRunnable();
        Thread myThread2=new Thread(myRunnable);
        myThread2.start();

第三种方式:匿名内部类

对于实现接口的一种改造

public static void main2(String[] args) {
        //通过匿名内部类进行改造
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("实现Runnable接口实现的多线程");
                }
            });
            thread.start();
        }
    }

第四种:lambda表达式

public static void main(String[] args) {
        //通过lambda表达式进行改造
        for(int i=0;i<10;i++){
            Thread thread=new Thread(()->{
                System.out.println("实现Runnable接口实现的多线程");
            });
            thread.start();
        }

    }

对于lambda表达式的理解,前面不是说Runnable告诉我们要执行什么嘛,但是为什么这里的lambda表达式里面并没有重写Runnable里面的run方法,那我们还知道要执行什么任务吗:因为编译器会看它是什么位置来判定它实现那个函数式接口,毕竟lambda表达式只有是函数式接口(有且仅有一个抽象方法)的时候才能使用,Thread有构造器:,然后编译器知道这里期望传入一个Runnable类型对象,刚好我们写的lambda的()里面没有参数,对应了Runnable接口中run()也是没有参数的,所以匹配上了,所以编译器会自动判定这就是实现了run的重写

二,多线程的相关讨论以及方法

(在我们这篇博客中会在适当位置引入一些思考)

思考1:

在我们创建多线程的代码中,我们会发现Thread和Runnable并不需要导包

答:因为他们属于Java.lang里面的,这个是能被默认导包的,所以我们没有在代码中看见像是import Thread之类的

前台线程以及后台线程

前台线程:我们一般创建的默认是前台线程,前台线程就相当于一些骨干核心,当所有前台线程结束的时候,我们进程也就会结束

后台线程:也称为守护线程,后台线程的结束不会影响进程的结束,但是前台线程都结束了,后台线程也会结束

当然我们也可以在线程启动前改变种类,用seeDeamon这个方法

思考2:

对于start这个操作的理解:

当我们创建线程的时候,是重写run方法,但是正在让线程启动的而是start方法,start这个操作本质上会调用操作系统提供的api,在操作系统内部创建线程,所谓api也就是别人提前写好的方法,类以及接口,可以直接拿来使用,操作系统内部通过pcb来捕获,通过链表组织起来

pcb是进程的描述块,一个进程对应一个pcb

tcb是线程的描述块,一个线程对应一个tcb

interrupt和sleep

interrupt可以用来设置标志位,还可以用来唤醒sleep类似的阻塞方法(抛出异常)

sleep()是用来阻塞的,比如你想让这个线程等待多少秒之后再运行,单位为ms,并且你在哪个线程里面sleep,这个sleep才会在哪执行,并且他是Thread类中用static修饰的方法,只能通过Thread.sleep();的方式来使用,并且要抛异常或者try catch

public static void main1(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (true){
            System.out.println("hello");
               try {
                Thread.sleep(1000);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(200);
        System.out.println("interrupt");
        t.interrupt();
    }

对代码进行解释:我们通过lambda表达式创建了一个线程t,里面会一直执行打印hello这个操作,我们上来首先启动t线程,然后在main线程里面执行sleep,会先阻塞200毫秒,在这个时候会先执行t线程的打印,然后main线程里面打印interrupt,但是按理来说我们预期的应该t线程还休眠800毫秒吧,应该先抛异常,那为什么还打印了一个呢,首先我们要搞清楚的就是只有当对应t线程处于sleep的时候,才会抛出异常,但是当我们main线程执行完,cpu的调度还暂时在main线程,要进入t线程还需要cpu的调度,会花去一些时间,所以当我们进入t线程的时候,sleep已经阻塞完了,需要再打印一个hello进入阻塞时候才会抛出异常,再下面就是while里面的判定一直为true,并且我们并没有退出程序的操作,所以后续还是打印hello

public static void main2(String[] args) {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("运行中");
            }
            System.out.println("结束");
        });
        t.start();
        try {
            Thread.sleep(400);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        t.interrupt();
    }

interrupt作为标志位的展示,代码中我们先启动t线程,然后在main线程里面阻塞400毫秒,这期间不断打印运行中,程序默认最开始我们的isInterrupted为false,通过interrupt方法变为true,程序结束

再总的理解一下start调用->sleep->interrupt的全过程(我用画图来解释)

TCB线程控制块(线程的所有信息)

PCB进程控制块(进程的所有信息)

线程等待(join)

线程等待可以干预两个线程的结束顺序,像前面谈到的sleep就是一种,我们接下来要谈的是join

join代表了死等,并且谁来调用join就是要等待他执行完毕,就像t.join就是死等到t线程结束

//join方法
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("线程执行"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        System.out.println("主线程前面的执行");
        try {
            t.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程后面的执行");
    }

并且sleep,join以及后面会谈到的wait这三者对于interrupt的阻塞以及抛出异常的机制是一样的

获取当前线程

Thread.currentThread

因为这个方法是Thread类的static方法,所以不可以使用对象来调用,必须使用类名来调用,并且获取到当前线程之后还可以继续调用来获取当前线程的id,名字等

 //获取当前线程
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            System.out.println(Thread.currentThread().getName());
            System.out.println(Thread.currentThread().getId());
            System.out.println(Thread.currentThread().getPriority());//线程优先级,默认是5
            System.out.println(Thread.currentThread().isDaemon());//是否是守护线程,默认是false
        },"可以给线程自定义名字");
        t.start();
    }

思考:用this.getName也可以调用到name,那么他和我们当前调用那么的区别在哪,并且this的使用场景在哪

//static 方法 /static 类,不能直接访问普通(非 static)的成员内部类;
    // 如果要在静态上下文里使用内部类,这个内部类本身必须是 static(静态内部类)。
    static class myThread extends Thread{
        @Override
        public void run(){
            System.out.println(this.getName());
            System.out.println(this.getId());
        }
    }
    //和this的区别
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                System.out.println(this.getName());
                System.out.println(this.getId());
            }
        }.start();
        myThread myThread=new myThread();
        myThread.start();
    }

this来获取必须符合匿名内部类或者继承Thread类才可以,然后重写run方法在run方法里面使用this,不能在runnable底下使用

三,线程状态

状态名称说明触发条件
NEW 新建新建线程对象,没有调用start()Thread t = new Thread();仅创建 Java 对象,操作系统没有内核 TCB,没有真正线程
RUNNABLE 可运行可运行。包含:正在 CPU 运行,或者等待 CPU 分配时间片(就绪)调用start();线程等待 IO 也处于 RUNNABLE;不区分 “运行中” 和 “就绪”,Java 全部归为 RUNNABLE
BLOCKED 阻塞等待获取 synchronized 锁,没拿到锁就卡在这竞争synchronized同步锁失败,等待监视器锁;只有synchronized才会进入 BLOCKED,Lock不会到这个状态
WAITING 无限等待无限等待,需要别的线程手动唤醒Object.wait()、Thread.join()、LockSupport.park();不会主动苏醒,必须等待另一个线程唤醒 (notify/unpark)
TIMED_WAITING 计时等待限时等待,时间到自动醒,也可以提前唤醒sleep(毫秒)、Object.wait(超时)、join(超时)、LockSupport.parkNanos()
TERMINATED 终止线程执行结束,死亡run () 正常执行完毕;或者线程异常结束;线程结束,内核 TCB 销毁,Java Thread 对象还在等待 GC,不能再次 start ()

四,线程安全(重点)

什么情况会引发线程安全

举个例子:

两个线程都尝试修改同一个变量

public class demo5 {
    private static int count=0;
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        });
        t1.start();
        t2.start();
        //等待两个线程全部执行完毕
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count);
    }
}

static保证两个两个线程修改的是同一个count,那按照我们的预期应该得到的结果是20000,可以实际的结果会比20000少一些,那这是为什么呢,因为我们线程的调度是随机的,我换一个执行过程的图来方便理解,其中count++这个动作可以简化为三个动作,第一:load,把内存中的数据加载到寄存器中,第二:add,把寄存器数据加一,第三:save,把寄存器中的数据写回内存

上面我只列举了两种情况,因为是随机调度,所以这三个步骤会被拆分为好多种情况组合在一起,所以几乎不可能出现全是1的情况,所以结果肯定比预期的要小一些

引起线程安全

所以经过上面的例子,我们可以总结和补充一下什么会引起线程安全

原因一:线程内部执行指令的调度顺序是随机的

原因二:多个线程针对同一个变量

原因三:修改操作不是原子的(就像count++要拆分为三个小步骤,这三个小步骤就能出现随机调度)

锁

针对第一个问题是操作系统的内核,我们控制不了,然后第二个又是我们经常需要的操作,所以我们只能针对第三个操作进行一些改进,所以我们引入了一个很关键的东西,那就是锁synchronized

锁可以将三个操作打包为一个整体,做到互斥以及独占,这是核心思想

//通过锁来解决这个线程安全问题
    public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                synchronized (locker){
                    count++;
                }
            }
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                synchronized (locker){
                    count++;
                }
            }
        });
        t1.start();
        t2.start();
        //等待两个线程全部执行完毕
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count);
    }

先解释这里锁的作用以及锁的使用:锁的()里面需要填写一下object的对象,所以我们需要自己创建一下locker对象,并且两把锁里面的object对象得是同一个,这样才会产生锁竞争(阻塞,防止插队),也就是t1线程完成了count++内部的三个完整操作,才会把锁解开,可以供其他线程使用,只要进入了锁的 { 就代表进入了锁,然后到 } 就代表了出锁,这里的锁并不是防止其他线程的调度,而是防止插队,调度到其他线程之后也会因为锁已经被使用还没放出来而进行阻塞等待,那肯定就有疑惑,我们的锁加到for循环外面会怎么样:加到for循环外部会使得t1线程先完成全部的++之后再把锁让出来给t2完成全部的++,结果都是一样的,但是这就没有看到线程的并发了

死锁

比如在同一个线程对象上加两把锁,根据上面说的锁竞争就会卡住,陷入死循环

还有一个经典模型(哲学家就餐)n个线程m把锁

就是一些哲学家围在一个圆形桌子就餐,每两个人之间有一根筷子,哲学家要不在思考人生,要不就拿筷子就餐,但是当所有哲学家同时拿起左边的筷子的时候,并且哲学家都很固执,不愿意放下筷子,这样子就会陷入卡死的情况

我们来总结一下死锁出现的必要原因:1,锁是互斥的,2:锁不可被抢占,3:请求保持(拿到一把锁同时尝试获取另一把锁),4:循环等待

1和2与代码结构相关,所以我们如果想要避免死锁的话最好对3和4下手,对于3我们可以避免嵌套锁,对于4我们可以约定加锁的顺序

volatile

对于4我们引入了关键字volatile,用来解决内存可见性引起的线程安全问题以及指令重排序的问题

内存可见性

//内存可见性以及指令重排序

public class demo6 {
   public static boolean flag=false;
    public static void main1(String[] args) {

        Thread t1=new Thread(()->{
            while (!flag){

            }
            System.out.println("检测flag已经变为false,退出循环");
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        t1.start();
        flag=true;
    }
    public static void main(String[] args) {

        Thread t1=new Thread(()->{
            while (!flag){

            }
            System.out.println("检测flag已经变为false,退出循环");
        });
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        flag=true;
    }

我们这里可以重点来理解一下背后的逻辑,以及这两种代码为什么会有差别

(1)这背后就会涉及到一个逻辑叫做编译器优化:就是在不改变代码逻辑的前提下,对于代码的一些优化,这是编译器背后自动会做的

(2)这就会引发一个什么问题呢,我们看main里面,我们将flag修改了但是死循环还在,依旧没有打印退出循环的那个sout,因为我们在主线程里面的修改前面设置了sleep,所以当任务开始执行的时候,会先疯狂的进入死循环,这个时候主线程里面还在sleep,所以因为好久都没有修改,所以编译器就将flag从内存放入到了寄存器当中,所以后续就算我们修改了,读到的也是寄存器当中的false,背后的逻辑就是读这个操作的时间远远大于比较以及跳转的时间,所以就将这个操作优化掉了,所以t线程里面压根看不到修改了

(3)那么上面这两种写法有什么区别呢,也就是sleep的位置不一样,那么最终的结果有区别吗,是有区别的,因为main1里面start启动之后紧接着的操作就是修改,所以循环还没来得急疯狂的跑,就已经把条件改掉了,所以根本不会死循环

指令重排序

这个也是编译器优化的,就是适当改变了一下执行的顺序

 //指令重排序
    public static int a=0;

    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (!flag){

            }
            System.out.println(a);
        });
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        a=10;
        flag=true;
    }

这里我们正常肯定是打印出10,但是因为指令重排序的缘故,flag=true可能会在执行的时候安排在a=10前面,这样可能会造成循环迅速跳出,然后马上打印最开始的0,毕竟读的时间还是长一点,可能没来得及读该之后的,不过指令重排序是概率也是可能发生,所以你自己试的时候可能并不会出现

针对上面的内存可见性以及指令重排序,我们可以在变量之前加上volatile关键字防止这两个编译器优化的操作

public static volatile boolean flag=false;

在上面的代码中只需要管好flag就好了,所以只需要在flag上面加volatile,具体情况具体分析

五,wait/notify

wait和notify是一起使用的,一个进行阻塞,一个进行唤醒,因为如果想要t1先执行1,然后t2再执行2的操作,join这样的等待就不行,因为join只能影响结束顺序

wait的具体操作:1,释放obj对象对应的锁,wait是放在锁内部使用的,2:等待其他线程的通知,进入阻塞,3:当通知到达时候,从阻塞队列重新回到就绪队列,并且重新获取到锁

wait有两种,一个是有时间的,一种就是没有时间的死等

wait和sleep的异同:都可以进行阻塞,也都可以设置时间,2:wait设计是为了notify,而sleep就是阻塞对应时间,3:wait一定要搭配锁,并且wait一进来就会释放锁,4:wait也可以用interrupt唤醒,不过做好是notify

public class demo7 {
    static volatile Object locker=new Object();
    static volatile boolean flag=true;
    static int num=0;
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (num<10){
                synchronized(locker){
                    while (!flag){
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                   if (num<10){
                       System.out.println("t1:"+num);
                       num++;
                       flag=false;
                       locker.notify();
                   }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        Thread t2=new Thread(()->{
            while (num<10){
                synchronized(locker){
                    while (flag){
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }

                    }
                    if (num<10){
                        System.out.println("t2:"+num);
                        num++;
                        flag=true;
                        locker.notify();
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }

手把手教你学Simulink——基础电机控制场景实例:基于Simulink的永磁同步发电机温度场耦合仿真 摘要:本文详细介绍了基于Simulink的永磁同步发电机(PMSG)电-热耦合仿真方法。通过Simscape Electrical搭建含热端口的PMSG模型,结合集总参数热网络(LPTN)实现损耗计算与温度反馈,分析了过载工况下温升对电阻和永磁体性能的影响。仿真结果表明:150%过载20秒会导致绕组温升132°C,电阻增加42%,磁链衰减10.5%。文章还提供了自然冷却与强迫风冷的对比数据,并建议通过多物理场耦合仿真预防实机过热问题。该方法可有效支撑高功率密度电机热设计,避免永磁体退磁风险。 阅读详情

相关推荐

OpenWrt南京电信PPPoE IPv6配置:从零搭建稳定双栈网络

本文详细指导如何在南京电信PPPoE宽带环境下,为OpenWrt路由器配置稳定的IPv6双栈网络。核心在于正确设置WAN6接口为DHCPv6客户端,并请求/60前缀,同时配置LAN口分发地址并放行防火墙关键流量,确保内网设备获得原生IPv6连接。

water的专栏 1342

javaEE初阶————多线程初阶(1)

线程就是一个“执行流”,可以理解为程序执行的最小单位;可以看成轻量级的进程;

2301_79083481的博客 4858

altium room 布局_ALtium 16相同重复模块PCB布局,复制布局走线

自己做了实际工程以后发现PCB器件名字后缀太长了,所以文章最后写了一下后缀的调整方法。第一种方法:1、建立子原理图如下,文件名CANM 2、之后新建一个顶层文件,文件名TOP,之后design—create sheet symbol form sheet or hdl。选择CANM,并放置到TOP中,如下 3、由于我们要多次调用CANM所以使用repeat命令修改如下: 4、之后根据自己的定义,连...

weixin_39609573的博客 5752

【JavaEE初阶】多线程初阶下部

这篇博客博主开始讲述多线程的下部,上部在博主的上一篇博客,需要的柚柚们可以看看~([链接在这里点击即可])

2301_80085602的博客 1616

手把手教你用Matlab搞定GRACE RL06数据读取:冯伟老师工具箱无GUI版实战

本文详细介绍了如何使用Matlab处理GRACE RL06数据,重点解析了冯伟教授工具箱的无GUI版实战操作。通过环境配置、核心函数改造和批处理技巧,帮助科研工作者高效完成重力场数据的读取与分析,特别适合服务器集群环境下的批量处理需求。

weixin_29313817的博客 200

【前端HTML生成条形码——MQ】

本文将展示前端HTML如何根据用户输入内容生成对应的条形码,附HTML和JS代码。代码如下(示例):

weixin_42588676的博客 1955

javaEE初阶————多线程初阶(5)

本期是多线程初阶的最后一篇文章了,下一篇就是多线程进阶的文章了,大家加油!

2301_79083481的博客 1966

DeepSeek提示词实战:从零到百的AI角色扮演指南(附100+场景模板)

本文提供了一份详尽的DeepSeek提示词实战指南,重点讲解AI角色扮演的核心技巧。通过“角色扮演四要素法”和五大核心场景模板,帮助用户从零开始掌握如何设计高质量提示词,有效提升AI输出的深度与精度,并附赠100+实用场景模板供参考。

weixin_29305337的博客 583

javaEE初阶————多线程初阶(2)

今天给大家带来第二期啦,保证给大家讲懂嗷;

2301_79083481的博客 1635

PCL K4PCS算法实现点云粗配准【2026最新版】

K4PCS算法实现点云粗配准。博客长期更新,本文最近一次更新时间为:2026年5月4日。

点云侠的博客 42万+

XGBoost原生接口和Sklearn接口参数详解

XGBoost原生接口和Sklearn接口参数详解

算法工程师 2656

多线程-初阶

/利用阻塞队列来组织任务//创建一个扫描队列执行任务的线程//获取任务并判断是否执行} else {//如果该任务等待时间过长,该线程频繁调度资源,白忙活一段时间,则计算等待时间//如果其中有其他新任务添加,则需要立马唤醒});//利用一个后台线程,短时间内不断唤醒扫描的线程try {},"后台线程");//提供一个提交任务的方法//由于该方法中有锁,如果其他线程不放锁,会导致put没法唤醒阻塞队列,那么造成死锁,双方一直等待。

2301_80791093的博客 1050

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

javaEE初阶————多线程初阶(4)

这又是什么新的神奇玩意呢,我们先不谈单例模式,先来谈谈设计模式,什么是设计模式呢,我们只需要用设计模式就好了,而大佬们考虑的就多了,这些设计模式就像棋谱,只要按照棋谱来下,你的水平就不会太差,设计模式就是大佬们给我们小卡拉米设计的计算机棋谱,而单例模式就是其中之一,还有很多的设计模式;那么什么是单例模式呢?单例模式就是保证某个类在程序中只存在一份示例这是为什么,就像DataSource,我们用数据库的时候有一份就好了,假设这个类实例化一次的开销就是几百个g,这样的开销可承担不起;

2301_79083481的博客 2633

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

JavaEE-初阶-多线程初阶

但是之前的方法有缺陷,会导致cpu资源被占用严重,所以在判断到队列为空和时间未到都会开始阻塞,而时间未到的阻塞还会另外设定一个时间,这个时间就是距离实际执行方法时间的差值,尽管线程被唤醒可能还是没有到执行时间,因为是优先级队列,唤醒的是最早执行的,那么也不过是再进去while循环做一次时间和队列为空判断,对于长时间循环而言,这些消耗微不足道。比较重要的就是判断后台线程和判断是否存活的方法。在一些情况下,有要求,线程要有结束的先后顺序,那么就可以通过线程的对象,来等待线程,来做到线程的结束顺序变为可控。

2401_82609762的博客 712

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【多线程初阶】详解线程池(上)

线程池 & ThreadPoolExecution & 七个参数含义 & 初识工厂模式 & 拒绝策略

2503_90322569的博客 235

多线程(初阶)——多线程基础

这个方法让JVM去操作申请一个真实的PCB,这就与操作系统扯上关系了,操作系统就能执行我们自己定义的这个线程任务了。

WangSir 734

【Java EE初阶 --- 多线程(初阶)】多线程的基本内容

既然有多线程,那么是不是也应该有单线程?答案是肯定的。其实单线程也叫进程,由上篇博客知道,进程中有多个线程,那么只有一个线程的进程就是单线程。我们的main主线程,之前的认知,main方法执行完毕,程序(进程)就结束了,那是因为之前没有接触到多线程程序,这篇博客将为我们认识多线程。。。

optimistic_chen的博客 1260

javaEE初阶————多线程初阶(3)

大家新年快乐呀,今天是第三期啦,大家前几期的内容掌握的怎么样啦?

2301_79083481的博客 1124

JavaEE初阶---多线程(初阶)

总结线程安全五大问题:1.系统随机调度/抢占式执行——万恶之源,无能为力;2.多个线程同时修改一个变量——部分规避;3.修改操作不是原子的——加锁可以改善 synchronized;4.内存可见性——由于编译器/JVM/操作系统误判,非正常优化了;5.指令重排序——使用volatile可以防止编译器进行指令重排序 .咱们先来说说 , 为什么要有线程池 . 进程本身已经能做到并发编程了 , 为啥还要有线程?进程太重量了 . 创建和销毁成本都比较高(需要申请释放资源).

baijaiyu的博客 873

【JavaEE】 多线程-初阶

多个线程组成了一个进程,线程好比是一跟光纤中的一个玻璃丝,进程是整根光纤。一个进程中的线程共享这个进程中的资源(内存、硬盘)

这个人很勤快,但还是什么也没有。 466

Java多线程编程初阶指南

实现方式代码行数可复用性灵活性推荐指数继承Thread5-10低差★☆☆☆☆常规Runnable6-8高中★★★☆☆匿名内部类3-5低高★★★★☆Lambda表达式1-3中极高★★★★★​核心定义​​:当多个线程并发访问共享资源时,程序仍能保持正确行为的状态​​黄金准则​原子性(Atomicity)保证可见性(Visibility)保证有序性(Ordering)保证使用没有共享资源的模型适用共享资源只读,不写的模型不需要写共享资源的模型。

2301_80720675的博客 1570

【Java EE初阶 --- 多线程(初阶)】多线程的实现案例

之前博客对多线程的是什么和基本内容都有详细了解,目前对于多线程的运用还很浅显,不能发挥出多线程应有的实力。这篇博客将带来多线程的基本应用,它会用到什么地方?又会带来什么高效的运行效率?我们又会学到什么?诸位尽情期待…

optimistic_chen的博客 1243

java初阶多线程2

String- 不可变性:String是不可变对象,创建后自身不能修改。设计为不可变的原因之一是天然的线程安全 ,且不可变对象方便存放到常量池中,其hashCode是固定值,便于和哈希表结合作为hash的key。- 示例: new String 创建的对象本身不能修改。- StringBuffer:本身可被修改,但通过加锁等机制解决线程安全问题。- StringBuilder:线程不安全,适用于单线程环境下的字符串操作。

j202312101422的博客 1052

java初阶多线程1

讲解Java多线程知识,从基础创建方式、常用属性方法,到线程状态转换,再深入到线程安全核心问题。理解这些内容,有助于开发者在Java多线程编程时,正确创建和管理线程,合理处理线程间协作与同步,规避线程安全风险,编写出高效、稳定的多线程程序。

j202312101422的博客 402
上一篇: MySQL数据库基础(从零到一)
下一篇: Java多线程四个案例
忙着追剧的村长
博客等级 码龄1年 586粉丝 50原创
评论 13
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值