当前位置: 首页 > wzjs >正文

移动互联与网站开发广州品牌营销服务

移动互联与网站开发,广州品牌营销服务,广州越秀区最新疫情,海外网络连接目录 前言1.线程安全的定义2.线程安全问题产生的原因2.1 多个线程修改一个变量2.2 修改操作不是原子的2.3 内存可见性引起的线程安全问题 3.解决线程安全问题的方法3.1 通过synchronized关键字加锁3.2 使用volatile关键字 总结 前言 在使用多线程的时候,难免会出现…

目录

  • 前言
  • 1.线程安全的定义
  • 2.线程安全问题产生的原因
    • 2.1 多个线程修改一个变量
    • 2.2 修改操作不是原子的
    • 2.3 内存可见性引起的线程安全问题
  • 3.解决线程安全问题的方法
    • 3.1 通过synchronized关键字加锁
    • 3.2 使用volatile关键字
  • 总结

前言

在使用多线程的时候,难免会出现一些问题,会产生线程安全问题,本篇文章就来简单讲述一下线程安全问题产生的原因和对应的解决方案。

1.线程安全的定义

线程安全是指当多个线程同时访问某个对象或方法时,不会出现数据不一致、逻辑错误或者意外结果的情况。即当把一个单线程执行的程序修改成一个多线程程序,产生的结果要和原来一样,如果不一样,则可以认为出现了线程安全问题。

2.线程安全问题产生的原因

造成线程不安全有各种各样的原因,但是导致线程不安全的根本原因就是线程的调度是随机的。下面还有一些常见的原因。

2.1 多个线程修改一个变量

多个线程同时访问共享资源(比如说同一个变量)可能会产生竞争状态,比如一个线程读取的是1,然后改成2,还没有保存,另一个线程也来读取,此时读取的还是1,这就会导致每次多线程执行完毕的时候,得出的结果可能都不一样。

2.2 修改操作不是原子的

就比如说对变量进行++操作,在java语句中是一行代码,实际上是三个操作:读取内存数据到CPU;对数据进行更新;然后将数据写回CPU。
在进行多线程操作的时候,可能就会因为操作顺序的抢占而产生线程安全问题

2.3 内存可见性引起的线程安全问题

下面先给出一段代码:

public class Demo17 {private static int flag = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while(flag == 0){//do nothing}System.out.println("t1线程结束");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("请输入一个数字:");flag = scanner.nextInt();System.out.println("t2线程结束");});t1.start();t2.start();t1.join();t2.join();}
}

我们来尝试运行一下
我们输入一个非零数字,理论上应该弹出t1线程结束和t2线程结束,我们来看结果:
在这里插入图片描述
我们发现并没有显示预期的结果,这是为什么?
这里就是由于”内存可见性“。
意思就是这里t2对flag进行修改,但是t1没有感知到,就是t1”没有看见“,一直认为flag还是0。
这就是内存可见性问题。
这是由于编译器优化所产生的问题,在代码编译时,编译器会进行一些优化策略,对代码进行优化,来提高程序的效率,这里是由于编译器优化出现了误判,从而导致代码的逻辑发生了改变,上面的程序就是由于此原因而出现问题,在t1的反复循环中,flag会被存进寄存器里来提高读取效率,不通过内存来读,而t2把通过内存把flag的数值变化后,t1仍然读的是原来存取到寄存器中的‘0’的值,这就是t1线程“看不见”flag的值变化的原因。

3.解决线程安全问题的方法

上述的根本原因不好解决,我们可以通过把修改操作变成”原子的“ 来解决线程安全问题

3.1 通过synchronized关键字加锁

可以通过加锁的方式将多段代码变成一个整体,让其它的线程无法进行干扰。我们通过synchronized关键字来实现锁。
比如我们对如下代码进行分析:

public class Demo14 {public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count++;}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = " + count);}
}

可以看出这里的操作并不是原子的,所以并不能如愿获得100000的结果。
我们就需要使用synchronized关键字来进行加锁,方法如下:

package thread;/*** @author Wind* @date 2025-04-13*/
public class Demo14 {public static int count = 0;private static Object locker = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (locker) {count++;}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {synchronized (locker){count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = " + count);}
}

将count++操作进行加锁,在执行count++前,后台会将语句lock,在调用完count++操作后,进行unlock操作,假设当t1加锁后,t2要进行count++前,要执行加锁,此时加锁不成功,会阻塞等待,知道t1进行unlock,这样就不会导致多个线程对同一个变量进行修改。现在观看结果,可以发现结果正确:
在这里插入图片描述
synchronized还可以用来修饰方法,同时也能起到加锁的作用:

public class Demo14 {static class Counter{private int count = 0;private Object locker = new Object();synchronized public void add() {count++;}}public static void main(String[] args) throws InterruptedException {Counter counter = new Counter();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.add();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {counter.add();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = " + counter.count);}
}

这里实际上就是对add方法加锁。

synchronized也可以对静态方法加锁,此时可以认为是对类对象进行加锁。

3.2 使用volatile关键字

使用volatile关键字可以解决上面所出现的内存可见性问题。
volatile关键字用于修饰变量,用于告诉编译器,这个变量是经常性变化的,使编译器不对变量进行上面内存可见性中所出现的优化。
修改后的代码(将flag用volatile修饰):

public class Demo17 {private static volatile int flag = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while(flag == 0){//do nothing}System.out.println("t1线程结束");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("请输入一个数字:");flag = scanner.nextInt();System.out.println("t2线程结束");});t1.start();t2.start();t1.join();t2.join();}
}

下面看运行结果:
在这里插入图片描述
可以看到,程序正常结束。
此外还需要注意的是,volatile虽然可以解决内存可见性问题,但它本身不具有原子性,无法解决不是原子性的问题。

总结

以上就是对线程安全的简单介绍,希望这篇文章能帮助你更加熟练的运用多线程。

http://www.dtcms.com/wzjs/93116.html

相关文章:

  • 东莞网站建设信科专注于品牌营销服务
  • 免费建网站哪个平台好使用百度地图导航收费吗
  • 做网站的客户个人自己免费建网站
  • 河北网站建设品牌大全seo优化关键词放多少合适
  • asp个人网站下载网络推广怎么找客户资源
  • 做网站使用字体图标网站的营销推广方案
  • 无锡新区网站建设上海网站排名优化
  • 网站怎么做排行榜百度服务中心人工客服
  • 网站 建设 函郑州专业网站建设公司
  • 旅游网站建设规划书主题2023年10月爆发新冠
  • 国内有做网游评测的网站么微博推广有用吗
  • 建网站的企业seo高手是怎样炼成的
  • 视频网站开发背景百度一下首页极简版
  • asp动态网站建设毕业设计外贸网站搭建推广
  • 找做网站的上什么app百度网站域名注册
  • 怎么样做美术招生信息网站网络推广专员岗位职责
  • 安康网站建设公司电话南宁seo公司哪家好
  • 包装材料网站建设广告软文范例大全100字
  • 自己做营销网站重庆seo小z博客
  • ppt 做的最好的网站有哪些营销型网站seo
  • 自己怎么做商城网站吗网站seo快速排名优化的软件
  • 业余从事网站开发桂林seo
  • 网站建设资料百度云seo优化工作怎么样
  • 网站抓取qq号码原理足球积分排行榜最新
  • 怎么让百度搜到网站windows永久禁止更新
  • 深圳网站建设公司哪家可以建app怎么找需要推广的商家
  • 沧州网站建设报价广州seo公司品牌
  • 自己做网站地图小红书关键词优化
  • 移动端网站开发框架廊坊seo排名收费
  • 是不是做网站就能赚钱网站如何提升seo排名