多线程

【java多线程编程】三种多线程的实现方式 文章目录前言进程与线程继承Thread类,实现多线程FAQ 为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?基于Runnable接口实现多线程Thread 与 Runnable 的关系Callable实现多线程线程运行状态 前言       在java语言最大的特点是支持多线程的开发(也是为数不多... 阅读详情

一、可见性
   多个线程对同一个变量(称为:共享变量)进行操作,但是这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化,当线程要处理该变量时,多个处理器会将变量从主存复制一份分别存储在自己的存储器中,等到进行完操作后,再赋值回主存。
   
  这样做的好处是提高了运行的速度,同样优化带来的问题之一是变量可见性——如果线程t1与线程t2分别被安排在了不同的处理器上面,那么t1与t2对于变量A的修改时相互不可见,如果t1给A赋值,然后t2又赋新值,那么t2的操作就将t1的操作覆盖掉了,这样会产生不可预料的结果。因此,需要保证变量的可见性(一个线程对共享变量值的修改,能够及时地被其它线程看到)。

  注意:共享数据的访问权限必须定义为private

  多线程操作共享变量实现可见性过程JVM的内存模型如下:
这里写图片描述
 

  所有的变量都存储在主内存中每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)。

 JVM模型两条规定:

   1、线程对共享变量的所有操作必须在自己的内存中进行,不能直接从主内存中读写

   2、不同线程之间无法直接访问其它线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成

  那么共享变量应该怎样实现可见性呢?  

  比如:线程1对共享变量的修改想要被线程2及时看到,必须经过如下2步操作:
  把工作内存1中的更新过的共享变量刷新到主内存中,再将主内存中最新的共享变量的值更新到2的工作内存中。

  初始值X=0,线程1将X=1,到其他线程X值的更改,如下图的更改过程。
这里写图片描述
                  

   因此,要实现共享变量的可见性,必须保证两点:
   线程修改后的共享变量值能够及时从工作内存刷新到主内存中;
   其他线程能够及时的把共享变量的最新值从主内存更新到自己的工作内存中。
   在Java语言层面支持的可见性实现原理方式有synchronize和volatile。

备注:

导致共享变量在线程间不可见的原因:
  线程的交叉执行;
  重排序结合线程的交叉执行;
  共享变量更新后的值没有在工作内存与主内存及时更新。

二、synchronize 

  能够实现代码的原子性(同步)和 内存的可见性。

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
原子性就像数据库里面的事务一样,他们是一个团队,同生共死。其实理解原子性非常简单,我们看下面一个简单的例子即可:

i = 0;---1 
j = i ; ---2 
i++; ---3 
i = j + 1; ---4

上面四个操作,有哪个几个是原子操作,那几个不是?如果不是很理解,可能会认为都是原子性操作,其实只有1才是原子操作,其余均不是。

1—在Java中,对基本数据类型的变量和赋值操作都是原子性操作; 
2—包含了两个操作:读取i,将i值赋值给j 
3—包含了三个操作:读取i值、i + 1 、将+1结果赋值给i; 
4—同三一样
  在单线程环境下我们可以认为整个步骤都是原子性操作,但是在多线程环境下则不同,Java只保证了基本数据类型的变量和赋值操作才是原子性的(注:在32位的JDK环境下,对64位数据的读取不是原子性操作*,如long、double)。要想在多线程环境下保证原子性,则可以通过锁、synchronized来确保。

   JVM对其的两条规定:
  线程解锁前,必须把共享变量的最新值刷新到主内存中;
  线程枷锁前,将清空工作内存中的共享变量的值,从而使用共享变量时需要从主内存中重新读取新的值。(枷锁与解锁需要是同一把锁)。
  线程解锁前对共享线程变量的修改在下次枷锁时对其他线程可见。

  线程 执行互斥代码的过程:    

1、获得互斥锁;
2、清空工作内存;
3、从主内存拷贝变量的最新的值到工作内存;
4、执行代码;
5、将更改后的共享变量的值刷新到主内存;
6、释放互斥锁。

三、volatile

  保证变量的可见性,不能保证变量的符合操作原子性。

实现内存可见:
       深入的说:通过加入内存屏障和禁止重排序优化实现。对其变量执行写操作时,会在写操作后加入一条store屏障指令;对其进行读操作时,会在读操作前加入一条load屏障指令。
       通俗的说:volatile变量在每次被线程访问时,都强迫从主线程中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存中,这样任何时刻,不同的线程总能看到该变量最新的值。

  线程写volatile变量的过程:
  1、改变线程工作内存中volatile变量副本的值;
    2、将改变后的副本的值从工作内存刷新到主内存。
    3、线程读volatile变量的过程:
    4、从主内存中读取volatile变量的最新值到线程的工作内存中;
    5、从工作内存中读取volatile变量的副本。

  验证 volatile 可以保证原子性。代码如下:

复制代码

/**
* 验证 volatile 是否保证原子性
* @author Administrator
*
*/
public class VolatileDemo {
private volatile int number = 0;
public int getNumber(){
return this.number;
}
public void increase(){
try {
//更好的输出效果
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.number++;
}

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    final VolatileDemo volDemo = new VolatileDemo();
    //实现500次自增
    for(int i = 0 ; i < 500 ; i++){
        new Thread(new Runnable() {
            @Override
            public void run() {
                volDemo.increase();
            }
        }).start();
    }
    //如果还有子线程在运行,主线程就让出CPU资源,
    //直到所有的子线程都运行完了,主线程再继续往下执行
    while(Thread.activeCount() > 1){
        Thread.yield();
    }
    System.out.println("number : " + volDemo.getNumber());
}

}

复制代码

  上述代码,理想情况或单线程时输出结果应该为:500,然而实际输出的结果是小于500的数字(多执行几遍)。这是因为 number++ 不是原子操作,会造成多个线程交叉执行。

方法一: synchronized
复制代码

public void increase(){
try {
//更好的输出效果
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized(this){
this.number++;
}
}

复制代码

  当然,也可以 public synchronized int increase(){…} 但是这样造成程序性能更加低效。

方法二: java.util.concurrent.locks.ReentrantLock
复制代码

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class VolatileDemo {
private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber(){
return this.number;
}
public void increase(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.lock();
try {
this.number++;
} finally {
lock.unlock();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo = new VolatileDemo();
for(int i = 0 ; i < 500 ; i++){
new Thread(new Runnable() {

            @Override
            public void run() {
                volDemo.increase();
            }
        }).start();
    }
    //如果还有子线程在运行,主线程就让出CPU资源,
    //直到所有的子线程都运行完了,主线程再继续往下执行
    while(Thread.activeCount() > 1){
        Thread.yield();
    }
    System.out.println("number : " + volDemo.getNumber());
}

}

复制代码

方法三:java.util.concurrent.atomic.AtomicInteger;
复制代码

import java.util.concurrent.atomic.AtomicInteger;
public class VolatileDemo {
private static AtomicInteger a = new AtomicInteger();
public int getNumber(){
return a.get();
}
public void increase(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a.getAndIncrement();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final VolatileDemo volDemo = new VolatileDemo();
for(int i = 0 ; i < 500 ; i++){
new Thread(new Runnable() {

            @Override
            public void run() {
                volDemo.increase();
            }
        }).start();
    }
    //如果还有子线程在运行,主线程就让出CPU资源,
    //直到所有的子线程都运行完了,主线程再继续往下执行
    while(Thread.activeCount() > 1){
        Thread.yield();
    }
    System.out.println("number : " + volDemo.getNumber());
}

}

复制代码

  AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减,因此十分适合高并发情况下的使用。

java.util.concurrent中实现的原子操作类包括:AtomicBoolean、AtomicInteger、AtomicIntegerArray、AtomicLong、AtomicReference、 AtomicReferenceArray。

volatile适用场合

在多线程中安全的使用volatile变量必须同时满足两个条件:
  ①对变量的写入操作不依赖其当前值,如number++不可以,boolean变量可以
  ②该变量没有包含在具有其他变量的不变式中,如果有多个volatile变量,则每个volatile变量必须独立于其他的volatile变量

四、synchronize 与 volatile 比较

  1、volatile不需要枷锁,比synchronize更轻量级,不会堵塞程序;
  2、从内存可见角度讲,volatile读相当于枷锁,volatile写相当于解锁。
  3、synchronize既能保证可见性,又能保障原子性,而volatile只能保障可见性,不能保证原子性。
4、synchronize 使用更加广泛。

来自慕课网课程:细说Java多线程之内存可见性

Ruby 多线程 多线程是一种允许单个程序同时执行多个任务的技术。在传统的单线程程序中,任务通常是顺序执行的,这意味着如果一个任务需要较长时间才能完成,它会阻塞后续任务的执行。而在多线程环境中,程序可以同时运行多个线程,每个线程执行一个独立的任务,从而提高了程序的效率和响应速度。 阅读详情

相关推荐

【ECCV 2024】动态范围直方图自注意力DHSA,即插即用!

直方图自注意力机制,将空间特征按强度分段并进行分组,然后在各个分组内或分组间应用自注意力,以选择性地关注具有动态范围的空间特征,并处理长距离内相似退化的像素。

qq_43303160的博客 1844

JAVA多线程中访问变量问题

类变量(类里面static修饰的变量)保存在“方法区”实例变量(类里面的普通变量)保存在“堆”局部变量(方法里声明的变量)“虚拟机栈” “方法区”和“堆”都属于线程共享数据区,“虚拟机栈”属于线程私有数据区。 因此,局部变量是不能多个线程共享的,而类变量和实例变量是可以多个线程共享的。事实上,在java中,多线程间进行通信的唯一途径就是通过类变量和实例变量。也就是说

哦绝影 7359

联想Y9000P安装Ubuntu20.04记录

联想Y9000P 2022安装Ubuntu20.04问题记录

Legendyyy的博客 7115

java 多线程访问变量_Java多线程之间访问实例变量

自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。图 1 所示为不共享数据的示例,图 2 所示为共享数据的示例。图1 线程之间不共享数据实例图图2 线程间共享数据示例图例 1如图 1 所示,在不共享数据时每个线程都拥有自己作用域的变量,且多个线程之间相同变量名的值也不相同。下面创建一个示例演示这种特性。首先创建自定义的线程类 MyThrea...

weixin_30146085的博客 1216

手把手教你用XC7A35T FPGA搭建低成本机器视觉系统(含开发板选型指南)

本文详细介绍了如何利用AMD(原Xilinx)Artix-7系列中的XC7A35T FPGA芯片,搭建一套低成本的机器视觉系统。内容涵盖从芯片选型深度剖析、开发板(如黑金AX7035B)对比指南,到使用Vivado创建工程、编写图像处理模块(如RGB转灰度),以及资源优化和与上位机(如OpenCV)协同工作的完整流程,旨在为开发者提供一套可实践的高性价比硬件加速视觉解决方案。

beta5的博客 351

标准恢复二极管整流器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告

报告页数: 150 图表数: 100 报告价格:¥16800 本文研究全球与中国市场标准恢复二极管整流器的发展现状及未来发展趋势,分别从生产和消费的角度分析标准恢复二极管整流器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。 主要生产商包括: Yangzhou Yangjie Vishay Intertechnology Power Semiconductors Inc Nexperia B

Fj69258的博客 5848

关于线程访问另外一个线程的变量问题

关于线程访问另外一个线程的变量问题 之前帮别人调错,遇到一个错误,就是一个线程A访问另外一个线程B的成员变量,并且线程A在构造方法中有线程B的引用,但是无论线程A怎么获取线程B的成员变量都是 null,但是在线程B初始化中已经给成员变量赋值了。 后面解决了很久,没解决出来,然后刚好看到了 指令重排序问题 ,下面我们来看一个例子 public class Test { int a = 0; boolean flag = false; public void writer(){

Hill__Cheung的博客 3559

Python读取Matlab v7.3数据

文章目录Matlab保存数据Python读取 v7.3 Mat Matlab保存数据 在matlab设置中,常规->MAT文件,选择MATLAB版本7.3或更高版本保存数据。 注意保存数据的文件名为 corre.mat,保存的数据数组为data; 后面python的读取中会用到这两个参数 Python读取 v7.3 Mat 先安装hdf5storage库,再使用以下代码,就可以读取到mat中的数据,注意 import hdf5storage import numpy as np dataDi

weixin_43121914的博客 1081

Java多线程(1)多线程访问成员变量与局部变量

1、多线程访问成员变量与局部变量说明 1、java多线程调用 单例类中一个的方法是不会排队的 因为jvm 在每个线程下,都有一份 对调用方法的引用。 2、多个线程调用的同一个对象的同一个方法: 如果方法里无成员变量,不受任何影响; 如果方法里有成员变量,只有读操作,不受影响,存在赋值操作,有影响 3、多线程调用同一个方法,局部变量会共享吗? 局部变量不会受多线程影响; 成员变量会受到多线程影响 jvm每个线程都拥有一个方法调用栈,用于跟踪线程中运行的一系列方法调用过程, 栈中的每个元素成为栈帧,

mingyuli的博客 6464

6、高通8155座舱平台--摄像头系统

在项目中遇到过,很多从手机转过来的工程师,上来就按手机那套调Camera,结果发现根本跑不通——为什么?我踩过这个坑——一开始想靠软件打时间戳来对齐,结果发现两个sensor的曝光时间不一样,时间戳根本对不上。我记得第一次调AWB时,怎么调颜色都不对,后来发现是BLC的参数没配好——黑电平偏了,后面所有颜色校正都是错的。上层应用说「我要拍张照」,HAL把它翻译成「ISP你给我处理这帧数据,sensor你给我曝光一次」。8155上推荐用camx,它是高通新一代的Camera框架,性能更好,而且支持多路同步。

woailuojp的博客 59

AI智能体丨使用Coze生成自媒体短视频分镜生成器(附保姆级教程)

【短视频分镜脚本生成器】是一款由Coze平台开发出来的智能体,可快速解析某音/某书视频或本地文件,自动生成专业分镜脚本。用户只需输入视频链接或上传文件,系统即会分析镜头节奏、叙事逻辑和画面内容,输出包含画面、模板和话术参考的标准化脚本(支持3分钟内视频)。该工具还能生成HTML网页和可下载图片,提供可视化呈现。工作流程包括视频解析、帧提取、AI画面理解、脚本生成和网页制作,大幅提升短视频创作效率。

李同学Lino的博客 1533

java中多线程修改一个变量

在 Java 中,如果多个线程同时修改同一个变量,可能会导致数据不一致的问题。因此,Java 提供了一些机制来保证多线程在修改变量时的安全性。 一种常用的方法是使用 synchronized 关键字,例如: public class MyClass { private int counter = 0; public synchronized void increment() { ...

weixin_42594427的博客 1469

PCL 点云八叉树射线相交

PCL 点云八叉树射线相交

dayuhaitang1的博客 850

Arduino的多线程

前言 本章节是如何使用Arduino 的多线程技术。截止目前,Arduino 的性能很棒,但是如果我们想在不同的时间间隔中,完成两件、多件的任务呢? 此时,我们就需要使用一种类多线程技术来处理项目。 Arduino 的多线程 \ 类多线程? 为了正确理解多线程技术和Arduino 之间的关系,我们需要理解为什么它不是真正的多线程。 在计算机CPU上,多线程应用程序...

TEST_PROGRAM_NONE 3万+

VC中sqlite的使用

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

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

多线程一,什么是多线程,创建多线程的几种方式

1 多线程相关概念 并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。 同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。 并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为

世上哪有什么岁月静好,不过是有人替你负重前行 2万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

多线程详解

​​学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:想系统/深入学习某技术知识点…一个人摸索学习很难坚持,想组团高效学习…想写博客但无从下手,急需写作干货注入能量…热爱写作,愿意让自己成为更好的人…...

m0_65144570的博客 2062

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Qt多线程

Qt 提供了多种方式来处理多线程编程,它不仅使多线程操作更易于管理,还与 Qt 的事件驱动架构紧密集成。下面是 Qt 多线程编程的详细介绍,包括关键类、模式以及常见的使用场景。

強雲的博客 2312

什么是多线程

什么是多线程? 多线程是一种同时执行多个线程的技术,它不允许进程或操作系统生成计算机中运行的程序的多个副本,而是在不创建程序多个副本的情况下,同时管理多个用户或同一用户的多个请求。 线程是轻量级进程,是处理的最小单元,同时运行多个线程称为多线程。因此,基本上,这是一种由多个处理器使用单个代码集的方法。 了解多线程 1.为了理解多线程,需要理解两个术语。 (1)线程:线程是进程的独立或基本单元。 (2)进程:正在执行的程序称为进程,一个进程中存在多个线程。 2.多线程中的执行是并发的和并行的。 (1)并发执行

qf2019的博客 1万+

一文讲清多线程和多线程同步

一个进程内多个线程并发执行的情况就叫多线程,每个线程是一个独立的执行流,多线程是一种编程模型,它与处理器无关、跟设计有关。并行计算:充分利用多核,提升整体吞吐,加快执行速度。后台任务处理:将后台线程和主线程分离,在特定场景它是不可或缺的,如:响应式用户界面、实时系统等。我们用2个例子作说明。同一进程内的多个线程会共享数据,对共享数据的并发访问会出现(竞争状况)。协调多个线程对共享数据的访问,避免出现数据不一致的情况。

excellent_fish_c的博客 1860

C++多线程-多线程调试

软件调试是我们软件开发过程中的重要一课。在前面,我们也讨论过程序调试,比如说这里。今天,我们还可以就软件调试多讲一些内容。比如说条件断点,数据断点,多线程断点等等。 #include <stdio.h> int value = 0; void test() { int total; int index; total = 0; ...

逍遥云恋 2327

php 怎么实现多线程,php 实现多线程

通过php的Socket方式实现php程序的多线程。php本身是不支持多线程的,那么如何在php中实现多线程呢?可以想一下,WEB服务器本身都是支持多线程的。每一个访问者,当访问WEB页面的时候,都将调用新的线程,通过这一点我们可以利用WEB服务器自身的线程来解决PHP不支持多线程的问题。下面给出通过 fsockopen() 建立socket连接,然后用 用fputs() 发送消息,来实现的PHP...

weixin_28756427的博客 1万+

Python 多线程

多线程是指程序中可以独立于其他部分并发执行的一系列指令。可以将它们视为进程中的不同单元,这些单元在被调度时可以独立完成任务。当线程需要等待缓慢的外部操作(如网络请求或磁盘访问)完成时,它们会暂时休眠,从而使调度器可以将时间分配给其他线程执行。进程是计算机程序的一个可执行实例。通常,一个进程是在单个控制流序列中执行的。进程是操作系统分配资源的基本单位,每个进程包含一个或多个线程。进程之间是隔离的,每个进程拥有自己的内存空间和系统资源。在这篇文章中,我们探讨了 Python 多线程的基本概念。

疯一样的码农 1427

C#多线程

C#中的多线程编程是开发高效并发应用程序的关键技术之一,它允许程序同时执行多个任务,从而提升应用程序的响应速度和性能。为了更好地理解C#中的多线程使用和定义,我们可以从以下几个方面来探讨:线程的基本概念、创建线程的方法、线程的状态管理以及线程同步机制。

C6666888的博客 1570

C++多线程

多线程(Multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。

yegu001的博客 1379

【Java多线程】Java多线程技能

到 1. 进程和多线程的概念及线程的优点 提到多线程这个技术就不得不提及“进程”这个概念,在“百度百科”中对进程的解释如下: 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。 进程是受操作系统管理的基本运行单元。 那什么是线程呢? 线程可以理解成是在

一定要站在自己热爱的生活里闪闪发光 4万+
上一篇: 线程的创建
下一篇: 关于项目中session一致性问题的解决
丿千寻
博客等级 码龄8年 8粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值