Java并发编程基础盘点(一)

Java并发编程基础小结 并发编程的核心问题与解决方案 本文从多线程的必要性、优势入手,深入分析了并发编程需要关注的三大核心问题:安全性问题(如数据竞争)、活跃性问题(如死锁、活锁)以及性能问题(如线程切换开销)。作者通过生动的代码示例和图示,展示了在多线程环境下可能出现的典型问题,并指出JVM中线程与进程的资源分配差异。文章还解释了程序计数器、虚拟机栈等线程私有区域的设计原理,以及如何利用"栈封闭"技术解决线程安全问题。全文为Java开发者提供了对并发编程本质问题的系统性思考框架。 阅读详情

在Java并发编程中,互斥性与可见性是两个非常重要的概念,本文将通过介绍synchronized和volatile关键字的使用,对并发环境下的互斥性与可见性的实现进行总结。

首先来看一下它们的概念:

互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。

可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

下面通过代码看一下具体的实现过程:

一、synchronized

在Java中,synchronized关键字是用来控制线程同步的。在多线程的环境下,使用synchronized能够控制代码不被多个线程同时执行。

1、同步非静态方法

被修饰的方法称为同步方法,这时的锁是当前类的实例对象。

1.1 多个线程访问相同对象的相同synchronized方法:

public class SynchronizedDemo1 {
    public synchronized  void access() {
        try {
            System.out.println(Thread.currentThread().getName()+" start");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName()+" end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        SynchronizedDemo1 demo01=new SynchronizedDemo1();
        for(int i=0;i<5;i++){
            new Thread(demo01::access).start();
        }
    }
}

运行结果:

可以看出,当多个线程对同一个对象的同步方法进行操作时,只有一个线程能够抢到锁。在一个线程获取了该对象的锁后,其他的线程无法获取该对象的锁,需要等待线程先把这个锁释放掉才能访问同步方法。

1.2 多个线程访问相同对象的不同synchronized方法:

public class SynchronizedDemo2 {
    public synchronized void access1() {
        try {
            System.out.println(Thread.currentThread().getName()+" in access1 start");
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName()+" in access1 end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public synchronized void access2() {
        try {
            System.out.println(Thread.currentThread().getName()+" in access1 start");
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName()+" in access1 end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        SynchronizedDemo2 test = new SynchronizedDemo2();
        new Thread(test::access1).start();
        new Thread(test::access2).start();
    }
}

运行结果:

由此可以确认,当线程访问synchronized修饰的任意方法时,如果当前对象被其他线程加锁,都需要等待其他线程先把当前的对象锁释放掉。

1.3 多个不同对象的线程访问synchronized方法:

public class SynchronizedDemo3 {
    public synchronized void access1() {
        try {
            System.out.println(Thread.currentThread().getName()+" start");
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName()+" end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        final SynchronizedDemo3 test1 = new SynchronizedDemo3();
        final SynchronizedDemo3 test2 = new SynchronizedDemo3();
        new Thread(test1::access1).start();
        new Thread(test2::access1).start();
    }
}

运行结果:

可以看出两个线程同时开始执行,这时因为两个线程属于不同的对象,而锁住的是类产生的实例对象,两个线程就获得了不同的锁。因此,不同对象产生的线程可以同时访问synchronized方法。

2、同步静态方法

静态方法是属于类的而不属于对象的 ,所以同样的, synchronized修饰的静态方法锁定的是这个类的class对象 。

public class SynchronizedDemo4 {
    public synchronized static void access() {
        try {
            System.out.println(Thread.currentThread().getName()+"  start");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName()+"  end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        for(int i=0;i<5;i++){
            new Thread(SynchronizedDemo4::access).start();
        }
    }
}

运行结果:

分析可知,当synchronized修饰静态方法时,线程之间也发生了互斥,当一个线程访问同步方法时,其他线程必须等待。因为当synchronized修饰静态方法时,锁是class对象,而不是类的实例对象。

3、同步代码块

被修饰的代码块称为同步代码块,其作用的范围是大括号括起来的代码,这时锁是括号中的对象。

那么为什么要使用同步代码块呢?在方法比较长,而需要同步的代码只有一小部分时,如果对整段方法进行同步操作,可能会造成等待时间过长。这时我们可以使用同步代码块对需要同步的代码进行包围,而无需对整个方法进行同步。

根据锁的对象不同,又可以分为以下两类:

a、以对象作为锁:

使用实例对象作为锁,即线程需要进入被synchronized的代码块时,必须持有该对象锁,而后来的线程则必须等待该对象的释放。

//以this为例
public void accessResources() {
    synchronized (this) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "  is running");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此处,因为this指的是当前对象,所以不能用在static方法上。

b、使用类的class对象作为锁:

public void accessResources() {
    synchronized (SynchroDemo5.class) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "  is running");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
    final SynchroDemo5 demo5 = new SynchroDemo5();
    for (int i = 0; i < 5; i++) {
        new Thread(demo5::accessResources).start();
    }
}

此时,有该class对象的所有的对象都共同使用这一个锁。

在当没有明确的对象作为锁时,只是想让一段代码同步时,则可以创建一个特殊的对象来充当锁,例如创建一个Object对象。

private final Object MUTEX =new Object();
public void methodName(){
   Synchronized(MUTEX ){
     //TODO
   }
}

看完了实现,那么synchronized底层的实现原理是怎样的呢?我们分同步代码块与同步方法来看:

反编译使用同步代码块的类生成的class文件:

这里使用了monitorenter和monitorexit对进入同步代码进行了控制。

monitorenter :

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

①如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者

②如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1

③如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit:

执行monitorexit的线程必须是monitor的持有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者,其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

反编译使用同步方法的类生成的class文件:

方法的同步并没有通过指令monitorenter和monitorexit来完成,相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标识符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

二、volatile

在介绍volatile前,先看一个例子:

public class VolatileTest {
    private static boolean flag=false;

    public void setFlag(){
        this.flag=true;
        System.out.println(Thread.currentThread().getName()+" change flag to true");
    }

    public void getFlag(){
        while(!flag){
        }
        System.out.println(Thread.currentThread().getName()+" get flag status change to true");
    }

    public static void main(String[] args) {
        VolatileTest test=new VolatileTest();
        new Thread(test::getFlag).start();
        
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        new Thread(test::setFlag).start();
    }
}

例子中使用两个线程来对boolean类型的flag进行修改和读取。讲道理当执行getFlag方法的线程检测到flag变为true时,应该退出循环并打印语句。但是看一下执行结果,你会发现只打印了setFlag中的语句,并且一直没有执行结束。

将flag加上volatile关键字再试一次:

 private static volatile boolean flag=false;

这时getFlag方法的线程检测到了flag的变化,并正常结束了程序。结合上面的例子,我们发现,当一个线程写数据,另一个线程读数据时,会存在数据不一致性的问题,而volatile的出现正好解决了这个问题。那么volatile究竟做了什么工作呢,这个时候就要引入java内存模型(JMM)来一探究竟了。

如上图中所示,java中运行的线程是不能直接读写主内存的变量,而是只能操作自己工作内存中的变量,然后再同步到主内存中。主内存是多个线程共享的,单线程间不共享工作内存,如果线程间需要通信,必须借助主内存中转来完成。

JMM控制中,又将对数据原子操作分为以下8个类别:

  • read(读取):从主内存读取数据

  • load(载入):将主内存读取的数据写入工作内存

  • use(使用):从工作内存读取数值来计算

  • assign(赋值):将计算好的值重新赋值到工作内存中

  • store(存储):将工作内存数据写入主内存

  • write(写入):将store过去的变量值赋值给主内存中的变量

  • lock(锁定):将主内存变量加锁,标识为线程独占状态

  • unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量

那么,我们之前举的例子就可以用下面的图来表示:

很明显,线程1无法跳出循环的原因是它读取的一直是自己工作内存中的flag,而没有获取到主内存中更新后的值。

为了解决缓存一致性问题,曾经使用过总线加锁的解决方案。具体来说,就是CPU从主内存读取数据到缓存,会在总线上进行数据加锁,这样其他CPU就没法去读写这个数据,直到这个CPU使用完数据释放锁之后其他CPU才能读取该数据。

但是这样一来,由于加锁的粒度太大,会造成阻塞时间过长,严重降低CPU的使用性能。因此在此基础上,行成了我们现在使用的MESI缓存一致性协议:

多个CPU从主内存读取同一个数据到各自的缓存,当其中某个CPU修改了缓存里的数据,该数据会马上同步回主内存,其他CPU通过总线嗅探机制可以感知到数据的变化而将自己缓存里的数据失效。

总的来说,就是在读操作时,不做任何事情,把内存中的数据读到缓存中。而在写操作时,发出信号通知其他的CPU将该变量置为无效,其他的CPU要访问这个变量的时候,只能从内存中获取。

给测试类配置启动参数,打印汇编指令到控制台:

-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileTest.setFlag

可以看出,在执行到修改flag的语句时,首先加入lock这一个前缀指令,实现了对缓存行的锁定。简单来说,就是:

lockflag=true write回主内存unlock

只有在执行写write操作时候才会加锁,相对总线对数据加锁,极大的降低了锁的粒度,只要不是在write过程中其他线程依然可以读取主内存中的数据,从而提高了CPU性能。

除此之外,volatile还能够实现指令的有序性。保证有序性是因为有时候会出现代码实际执行的顺序并不是我们输入的代码的顺序,那么为什么会出现这种情况呢,这里就有必要引入一下指令重排序:编译器为了优化程序的性能,会重新对字节码指令排序。指令重排序的基础是,编译器认为运行的结果一定是正常的。在单线程下,指令重排序对程序的帮助一定是正向的,可以很好的优化程序的性能,但是在多线程下,有可能因为指令重排序出现一些问题。

总的来说volatile实现有序性就是两点:

  • volatile之前的代码不能调整到它的后面

  • volatile之后的代码不能调整到它的前面

三、总结

最后,总结一下synchronized与volatile的特点以及区别:

  • 使用上的区别:volatile只能修饰变量,synchronized只能修饰方法和语句块;

  • 对原子性的保证:synchronized可以保证原子性,volatile不能保证原子性;

  • 对可见性的保证:都可以保证可见性,但实现原理不同。volatile对变量加了lock,synchronized使用monitorenter和monitorexit ;

  • 对有序性的保证:volatile能保证有序,synchronized可以保证有序性,但是代价变大(重量级),并发退化到串行执行;

  • 除此之外:synchronized引起阻塞,volatile不会引起阻塞。

如果您喜欢这篇文章,欢迎关注订阅我的微信公众号 “码农参上”,不定时发布最新文章

公众号后台回复“面试”获取海量大厂面试资料

Java并发编程基础篇(一)之线程 前言 并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最 大限度地并发执行。在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会 面临非常多的挑战,比如上下文切换的问题、死锁的问题,以及受限于硬件和软件的资源限制问题。这一块内容也是面试核心考点之一,所以博主将以线程为起点,从0到1一起与小伙伴们走去Java并发编程之路上走一遭! 正文 进程?线程?傻傻分不清? 何谓进程 进程通常是程序、应用的同义词。进程的本质是一个正在执行的程序,程序运行时系统会创建一个进程,并且给每 阅读详情

相关推荐

Java并发编程从入门到精通(pdf)(附源码)

原书作者it行业从业时间12年,多家it公司担任研发总监,技术总监(韩剑锋)。 如果你想成为一名架构师,资深的技术大牛,强烈推荐你读一读此书。 本书内容讲解深入浅出,掌握起来毫不费力。

Java多线程和并发基础(Java学习笔记)

Java多线程和并发编程基础

m0_59803718的博客 281

库存管理系统(java版)

库存管理系统底层数据库管理软件:SqlServer2005 本软件实现了对相应表的增删改查功能,对入库单、出库单的简单统计,以及能够从数据库导出数据信息,以文本形式保存的文件中。

java的非静态方法,[java]静态方法访问非静态方法的方法

是不是是有点拗口哈,在刚开始的开发中,经常会碰到"无法访问非静态方法",如下:Test.javapublic Test{public void notStatic(){System.out.println("我访问了非静态方法");}public static void main(String[] args){notStatic();//错误,无法访问}}解决办法有二:一 、设置其为静态方法//加...

weixin_35721520的博客 442

瑞芯微RK3588与FPGA的PCIe DMA通信性能优化实战

本文深入探讨了瑞芯微RK3588与FPGA之间PCIe DMA通信的性能优化实战。针对高速数据采集、AI推理等场景下的带宽瓶颈,文章从驱动配置、DMA描述符、中断处理、零拷贝技术及系统级调优等多个层面,提供了详细的优化策略与代码示例,旨在充分释放PCIe 3.0的理论带宽潜力,提升数据传输效率与稳定性。

sand8的博客 418

H3C模拟器HCL防火墙路由模式主备配置实验

摘要:本文详细介绍了网络拓扑规划与设备配置方案。运营商仅提供一个接口地址,通过虚IP技术实现双防火墙高可用部署:主备防火墙分别配置10.20.10.1/29和10.20.10.2/29作为实际上联地址,共享10.10.10.2/24虚IP;下联配置172.168.10.0/24网段,核心交换机配置VLAN接口及192.168.10.1、192.168.20.1两个下联接口。文章包含运营商路由器、防火墙(HA配置、VVRP设置)和核心交换机的具体命令行配置步骤,并强调需进行主备切换验证。整个方案实现了单运营商

LQ的博客 1122

Java并发编程之基础

文章目录1、线程与进程、并行并发、同步异步概念1.1、进程与线程1.2、 并行与并发1.3、同步和异步2、线程的创建 (重点)2.1、通过继承Thread创建线程2.2、使用Runnable配合Thread (推荐)2.3、使用FutureTask与Thread结合2.4、使用线程池来创建线程3、线程运行原理3.1、虚拟机栈与栈帧3.2、线程上下文切换(Thread Context Switch)3.3、Thread的常见方法3.4、sleep 与 yield3.5、线程优先级3.6、 join方法3.7

从基础到源码解析的学习记录 1385

双通道中频信号数字下变频及相位差估计(FPGA)

双通道中频信号数字下变频及相位差估计实验 一、上框图(通俗易懂) 二、实验要求 1、 用点频信号模拟中频信号:中频信号由DDS1和DDS2产生。具体产生方法见第2点。 2、 DDS1和 DDS2:调用IPcore里的数字信号处理中的NCO模块,工作时钟为100MHz,数据率即为100MHz,DDS1和DDS2的频率为:(学号后三位+500)Khz+f0(可设为20MHz);输出为16位;DDS1...

qq_39809425的博客 6943

Java并发编程基础(一篇入门)

1 并发编程简介 1.1 什么是并发编程 所谓并发编程是指在一台处理器上 “同时” 处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生。 并发编程,从程序设计的角度来说,是希望通过某些机制让计算机可以在一个时间段内,执行多个任务。从计算机 CPU 硬件层面来说,是一个或多个物理 CPU 在多个程序之间多路复用,提高对计算机资源的利用率。从调度算法角度来说,当任务数量多于 CPU 的核数时,并发编程能够通过操作系统的任务调度算法,实现多个任务一起执行。 1.2 并发编程的重要性 对于一个

行者无疆的博客 5854

谷歌Gemini模型免费API调用全攻略:从注册到实战(附代码示例)

本文详细介绍了如何免费调用谷歌Gemini模型的API,从注册获取API密钥到实战应用的全过程。内容涵盖环境准备、基础API调用、多模态处理、高级功能及生产环境实践,帮助开发者高效利用Gemini模型进行代码生成和复杂任务处理。附有代码示例,适合AI开发者和技术爱好者。

weixin_29212429的博客 1606

ANSYS APDL 后处理:用*GET和ETABLE命令把节点/单元应力结果导出到TXT文件(附完整命令流)

本文详细介绍了在ANSYS APDL中使用*GET和ETABLE命令导出节点和单元应力数据到TXT文件的完整方案。通过节点解和单元解的核心逻辑分析,提供了从基础到进阶的数据导出方法,包括节点应力、单元应力数据的提取与格式化输出,以及高级技巧如选择性导出与数据过滤。帮助工程师高效完成有限元分析后处理工作。

weixin_26805597的博客 634

全面详解Java并发编程:从基础到高级应用

Java并发编程提供了强大的工具和框架,帮助开发者有效管理多线程任务,提升应用程序性能。理解并掌握基本概念、线程管理、同步机制和高级并发工具,并遵循最佳实践,可以编写出高效、安全、可靠的并发程序。

微笑听雨 1万+

webview腾讯x5内核离线安装

离线集成腾讯TBS浏览器服务,彻底解决官方下载限流问题,永久免费

观千剑而后识器,操千曲而后晓声。 7913

Java 并发编程基础

Java 并发编程基础,涉及并发与并行的区别、进程与线程的区别、Java 线程基础、共享资源访问控制等内容。

leileiluoluo的博客 2177

手把手教你学Simulink——基于Simulink的无感FOC(滑模观测器+PLL)实战

摘要: 本文详细介绍了基于Simulink的无感FOC(滑模观测器+PLL)实现方法。主要内容包括:1)SMO+PLL原理,通过滑模观测器估算反电动势,结合PLL抑制抖振;2)系统架构,替代编码器信号;3)Simulink建模流程,涵盖Clarke变换、SMO离散化实现、PLL设计等关键步骤;4)参数整定指南和工程实践要点,如启动策略和故障处理。该方法具有成本低、可靠性高的优势,适用于家电、电动车等场景,仿真结果显示角度误差±5°,速度动态跟随良好。文章还提供了调试建议和常见问题解决方案。

MHD0815的博客 139

PCL 直通滤波

内容抄自优快云点云侠:[【2024最新版】PCL点云处理算法汇总(C++长期更新版)](https://blog.youkuaiyun.com/qq_36686437/article/details/114160640)。质量无忧,永久免费,可放心复制粘贴。

m0_51204289的博客 904

深入浅出Java并发编程:线程基础

通过本文的学习,我们详细探讨了Java并发编程中的线程基础,包括进程与线程的区别、线程的创建方式线程的生命周期守护线程与用户线程、以及线程优先级与调度策略。掌握这些基础知识是进一步学习并发编程的关键。在实际开发中,理解并正确使用这些概念可以帮助我们构建高效、稳定的多线程应用。然而,并发编程的复杂性远不止于此,后续我们还将深入探讨线程安全、锁机制、线程通信等高级主题。

在技术的广袤天地里,本博客如精准罗盘。剖析前沿科技,深掘代码奥秘,以精炼笔触,带您穿越复杂技术迷宫,速达知识彼岸。 1935

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

《Java并发编程的艺术》之 Java并发编程基础

《Java并发编程的艺术》之 Java并发编程基础 文章目录《Java并发编程的艺术》之 Java并发编程基础线程简介什么是线程代码清单4-1 MultiThread.java解析为什么要使用多线程更多的处理器核心更快的响应时间线程优先级代码清单4-2 Priority.java结果注意参数解析线程的状态注意Daemon线程注意注意启动和终止线程构造线程代码清单4-6 Thread.java启动线程注意理解中断代码清单4-7 Interrupted.java过期的suspend()、resume()和sto

weixin_44019182的博客 2496

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java 并发编程(一):简介

这篇文章的标题原本叫做——Java 并发编程(一):简介,作者名叫小二。但我在接到投稿时觉得这标题不够新颖,不够吸引读者的眼球,就在发文的时候强行修改了标题(也不咋滴)。 小二是一名 Java 程序员,就职于沉默公司,工龄是两年零一个月零三天。和刚毕业那会相比,编程能力已经大有提升,但领导老王一直没敢把并发编程的开发安排给小二,这让小二心里耿耿于怀。 这事不怪老王,小二心里很清楚:编写正确的程序...

沉默王二 4792

Java并发编程一 多线程

Java并发编程一 多线程Java并发编程是一个很热点的话题,无论在面试时候还是在高并发的场景中。都会涉及到Java的并发编程相关的知识。Java的并发编程有两个主要的基础知识,一个是线程安全另一个是线程间通信。 摘要 1,线程概念 2,Java线程的实现方式 3,Java线程状态流转介绍 4,Thread类中的常用方法分析

刘越洋子 2634

JAVA并发编程(一)并发编程线程基础

JAVA并发编程(一)并发编程线程基础1.1什么是线程?1.2线程的创建与运行1.2.1继承Thread类创建并运行线程1.2.2实现Runnable接口创建并运行线程1.2.3使用FutureTask创建并运行线程1.2.4小结1.3线程的状态转换 1.1什么是线程?       在讲什么是线程之前,我要提一个概念,那就是进程。那么什么...

qq_43080270的博客 532

【Java并发编程系列】- Java并发编程基础

默认情况下Java进程需要等其中所有线程全结束才会结束,但若设置某线程为守护线程,则在其他线程全结束的情况下,即使守护线程还没执行完,Java进程也会停止,守护线程自动被结束。多线程时,每个线程都会维护自己的栈和栈帧,每个线程只能有一个活动栈帧,对应当前正在执行的方法,方法执行完会返回到栈帧中方法出口地址的位置,然后从栈中pop出栈。直接使用 run 的方式,在mian中直接调用t1线程的run方法,则会是main方法调用执行run方法中的内容,t1会一直处于在新建状态。

m0_68383079的博客 788

Java并发编程第一篇(从零开始:一文读懂Java并发编程核心基础)

为什么需要用到并发?凡事总有好坏两面,这其中的权衡(trade-off)是什么,也就是说并发编程具有哪些缺点?以及在进行并发编程时,我们应该了解和掌握的核心概念又是什么?这篇文章将主要围绕这三个问题,为你揭开Java并发编程的神秘面纱。

Du_XiaoNan的博客 1045

java并发编程

什么是java并发编程如何进行并发编程

m0_64021225的博客 1724

文章一:Java并发编程基础

*进程(Process)**是操作系统分配资源和调度的基本单位,每个进程都有自己独立的内存空间、文件描述符和其他资源。进程之间的通信通常比较复杂,需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等。**线程(Thread)**是CPU调度的基本单位,一个进程可以包含多个线程。线程共享进程的内存空间和其他资源,但每个线程有自己的栈空间和程序计数器。线程之间的通信相对简单,因为它们共享相同的内存地址空间。

深度学习客 1013

Java并发编程(一) 并发编程基础

Java并发编程并发编程基础一、 进程和线程1. 进程2. 线程3. 进程与线程的区别:二、Java多线程编程1. Thread和Runnable1)实现Runnable接口2)继承Thread类并重写run方法3)匿名内部类3. Java中的线程状态4. Java中的线程状态转换5. 线程通信1)等待/通知2)管道输入/输出流3)Thread.join()4)ThreadLocal 并发编程基础...

qq_35017434的博客 861
上一篇: MyBatis 执行流程及源码解析
下一篇: Java并发编程基础盘点2-单例模式
码农参上
博客等级 码龄17年 9050粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值