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

做的烂的大网站企业网站建设需要哪些软件

做的烂的大网站,企业网站建设需要哪些软件,台州网站设计,网站建设公司如何选取目录 1.前言 2.正文 2.1线程安全类 2.2杂谈(介绍几个概念) 2.2.1内存可见性 2.2.2指令重排序 2.2.3线程饥饿 1. 什么是线程饥饿? 2. 线程饥饿的常见原因 2.2.4区分wait和sleep 2.4单例模式 2.4.1饿汉模式 2.4.2懒汉模式 2.4.2指…


目录

1.前言

2.正文

2.1线程安全类

2.2杂谈(介绍几个概念)

2.2.1内存可见性

2.2.2指令重排序

2.2.3线程饥饿

1. 什么是线程饥饿?

2. 线程饥饿的常见原因

2.2.4区分wait和sleep

2.4单例模式

2.4.1饿汉模式

2.4.2懒汉模式

2.4.2指令重排序与线程安全

3.小结


1.前言

哈喽大家好吖,今天继续给大家分享Java中多线程的学习,今天主要先给上文做个收尾以及讲解单例模式,那么废话不多说,让我们开始吧。

2.正文

2.1线程安全类

先再重新回顾一个概念,到底如何判断会涉及线程安全问题,凡是该方法涉及到修改数据的操作,而且没有内部进行加锁操作,这样就会导致线程安全问题,那么接下来就来详细介绍线程安全类以及线程不安全类:

常见的线程安全类

  1. 集合框架

    • Vector(同步方法)

    • Hashtable(同步方法)

    • CopyOnWriteArrayList(写时复制)

    • BlockingQueue 实现类(如 ArrayBlockingQueue,用于生产者-消费者模式)

  2. 原子类

    • AtomicIntegerAtomicLong

    • AtomicReferenceAtomicBoolean

  3. 工具类

    • String(不涉及修改)

    • StringBuffer(同步方法,线程安全版的 StringBuilder

    • Collections.synchronizedList()(包装非线程安全集合,如 ArrayList


常见的线程不安全类

  1. 集合框架(集合类本身没有进行任何加锁限制)

    • ArrayListLinkedList

    • HashMapHashSet

    • StringBuilder(非同步的字符序列操作)

  2. 日期格式化类

    • SimpleDateFormat(内部状态可变)

  3. 其他工具类

    • Random(共享种子可能导致竞争)

上述集合中,有的虽然有synchronized,但不推荐使用,因为加锁这个事情,是有代价的,一旦在代码中使用了锁,意味着代码可能会因为锁的竞争,产生阻塞,这样程序执行的效率会大打折扣,一旦造成线程阻塞,从cpu中调度走,啥时候才能回来执行就未知了。

2.2杂谈(介绍几个概念)

2.2.1内存可见性

内存可见性也是造成线程安全问题的原因之一,我们先附上一个代码:

import java.util.Scanner;public class test {public static int flag = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while(flag == 0){}System.out.println("t1线程结束");});Thread t2 = new Thread(()->{Scanner scanner = new Scanner(System.in);System.out.println("请输入flag值:");flag = scanner.nextInt();});t1.start();t2.start();}
}

我们尝试运行一下,结果发现:

我们修改了flag值,结果发现t1线程没有像我们预期的会结束线程,一个线程读取,一个线程修改,修改线程的值,并没有被线程读到,这就是内存可见性问题。

讲一下为什么:

研究 JDK 的大佬们,就希望通过让编译器 & JVM 对程序员写的代码,自动的进行优化
本来写的代码是进行 xxxxx,编译器/VM 会在你原有逻辑不变的前提下, 对你的代码进行调整
使程序效率更高。

编译器,虽然声称优化操作,是能够保证逻辑不变,尤其是在多线程的程序中,编译器的判断可能出现失误.可能导致编译器的优化,使优化后的逻辑,和优化前的逻辑出现细节上的偏差。

于是原因就显而易见了:

  1. 硬件架构影响

    • CPU缓存:每个线程可能在自己的CPU缓存中操作变量,而非直接读写主内存。

    • 指令重排序:编译器和处理器可能优化指令顺序以提高性能,导致代码执行顺序与预期不一致。

  2. Java内存模型(JMM)抽象

    • JMM规定所有变量存储在主内存,线程通过本地内存(缓存副本)操作变量。

    • 线程间通信需通过主内存完成,本地内存更新若未同步到主内存,其他线程无法感知变化。

于是上述代码我们这样稍作修改就可以了:

import java.util.Scanner;public class test {public static int flag = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while(flag == 0){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("t1线程结束");});Thread t2 = new Thread(()->{Scanner scanner = new Scanner(System.in);System.out.println("请输入flag值:");flag = scanner.nextInt();});t1.start();t2.start();}
}

结果如我们所愿。

那么我们不能一遇到内存可见性问题就选择sleep,那样会影响程序执行效率,所以说接下来我们引入一个关键字来解决这个问题:

volatile 是 Java 提供的一种轻量级的同步机制,主要解决 内存可见性 和 指令重排序 问题,但 不保证原子性。确保一个线程对 volatile 变量的修改对其他线程立即可见。

  • 问题根源:线程操作变量时可能使用本地缓存(如 CPU 缓存),而非直接读写主内存。

  • volatile 的解决:强制所有读写操作直接操作主内存,绕过线程本地缓存。

import java.util.Scanner;public class test {public volatile static int flag = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while(flag == 0){}System.out.println("t1线程结束");});Thread t2 = new Thread(()->{Scanner scanner = new Scanner(System.in);System.out.println("请输入flag值:");flag = scanner.nextInt();});t1.start();t2.start();}
}

结束!

2.2.2指令重排序

还记得上文提到的volatile关键字吗,里面的讲解提到了一个指令重排序的问题,那么这个问题是什么意思呢?这里先简单提及下,讲到单例模式时会有详细讲解:

指令重排序是指在不改变单线程程序执行结果的前提下,编译器或处理器通过调整指令的执行顺序来优化性能。


编译器进行指令重排序的原因:

  • 提高CPU利用率:减少流水线停顿,避免等待慢操作(如内存访问,上文提及)。

  • 优化缓存效率:通过局部性原理提高缓存命中率。

  • 并行执行指令:现代CPU的多级流水线和多核心架构需要指令级并行。


我们为什么之前没有遇到这个问题呢,因为我们在Java初阶的学习过程中大部分都是单线程环境下,只有在多线程环境下才会受到指令重排序的影响。

  • 可见性问题:一个线程的修改对另一个线程不可见。

  • 有序性问题:代码的实际执行顺序与预期不一致。

2.2.3线程饥饿

什么是线程饥饿呢?

1. 什么是线程饥饿?

线程饥饿指在多线程环境下,某个或某些线程长期无法获得所需的资源(如CPU时间片、锁、I/O等),导致其任务无法正常执行的现象。饥饿的线程可能永远等待,或执行进度远慢于其他线程。

关键特征

  • 非全局阻塞(其他线程仍正常运行)。

  • 由资源分配策略或调度机制引起。

  • 可能伴随优先级反转、资源竞争等问题。


2. 线程饥饿的常见原因

原因说明示例
高优先级线程抢占高优先级线程始终优先获得CPU时间片,低优先级线程长期无法执行。线程优先级设置不合理(如Java中setPriority(10)抢占低优先级线程)。
非公平锁竞争锁的获取策略不公平,某些线程始终竞争失败。synchronized关键字导致某些线程饥饿。
资源独占某个线程长期持有共享资源(如数据库连接、文件句柄),其他线程无法获取。未合理释放资源(如忘记关闭锁或未用try-finally块)。
任务调度策略缺陷任务队列设计不合理(如固定顺序的任务分配)。线程池使用无界队列或固定顺序提交任务。
通过合理设计资源分配策略和使用同步工具,可有效减少线程饥饿的发生,保障多线程程序的稳定性和性能。

2.2.4区分wait和sleep

在讲解单例模式前,再最后区分一下wait和sleep:

wait有等待时间,sleep也有等待时间,wait可以使用notify提前唤醒,sleep也可以使用Interrupt提前唤醒。


wait 和 sleep 最主要的区别,在于针对锁的操作.

  1. wait 必须要搭配锁.先加锁, 才能用 wait. sleep 不需要.
  2. 2)如果都是在 synchronized 内部使用, wait 会释放锁.sleep 不会释放锁~

2.4单例模式

单例模式是一种常用的软件设计模式,用于确保某个类只有一个实例,并且提供一个全局访问点。其中饿汉模式和懒汉模式是其中最经典的两种单例模式。

2.4.1饿汉模式

饿汉式单例在类加载时就创建实例,这种方式可以保证线程安全,但是实例的创建是立即进行的,可能会浪费资源。


 

class Singleton {private static Singleton instance = new Singleton(100);public static Singleton getInstance() {return instance;}private Singleton() {}private Singleton(int n) {}
}public class test2 {public static void main(String[] args) {Singleton t1 = Singleton.getInstance();Singleton t2 = Singleton.getInstance();System.out.println(t1 == t2);}
}

为了判断该代码仅创建了一个实例,我们创建t1和t2来判断是一个实例还是两个:

可以发现是一个实例。

2.4.2懒汉模式

懒和饿是相对的,一个是在程序一启动就创建好示例,另一个是尽可能晚的创建实例,以达到节省效率的目的。

懒汉式单例的特点是在需要的时候才创建实例,这种实现方式可以延迟实例的创建,节省资源。但是,如果多个线程同时访问getInstance方法,可能会导致多个实例的创建,因此需要进行同步处理。


class SingletonLazy{public static SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}
public class test3 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);// SingletonLazy s3 = new SingletonLazy();}
}

 

仿照着饿汉模式,我们仿佛就把正确的代码写出来了,但这里要抛出一个很重要的问题:这样的代码是否是线程安全的呢?

2.4.2指令重排序与线程安全

第一个饿汉模式,在getinstance方法中只涉及到return的读操作,不涉及到线程安全问题。然而懒汉模式的getinstance方法

创建实例时可能涉及到多线程的修改操作,并且一个if语句加上与一个创建示例的语句,这样就违背了原子性的原则。

在多线程环境下,如果有多个线程同时调用 getInstance() 方法,可能会在检查 instance == null 后,多个线程都进入 if 块并创建新的实例。这是因为多个线程可能在同一时间检查到 instancenull,从而都执行 new SingletonLazy(),导致创建多个实例。


所以我们就希望通过修改代码,使其避免上述问题。


但又有新的问题出现了,在多线程情况下,加锁会互相阻塞,影响执行效率,所以我们再进行修改:

此处最外层的if语句即为判断该实例是否已被创建,如果该实例以及被创建,就不需要进行获得锁操作,提升程序执行效率。


这样总会没问题了吧,不其实还有,有没有可能出现内存重排序问题呢,稳妥起见我们加上volatile

到这里就要呼应上文了,那指令重排序呢,现在就要讲了:

在创建实例时,要经过下面几个步骤:

  1. 申请内存空间
  2. 在空间上构造对象(初始化)
  3. 内存空间的首地址,赋值给引用变量

正常来说,这三个步骤按照 123这样的顺序来执行的,但是在指令重排序下,可能成为132 这样的顺序单线程环境下,123 还是 132 其实无所谓~~如果是13 2 这样的顺序执行,多线程环境下,可能会出现 bug !!

如果先进行1,3,那么很有可能出现尝试赋值时在对一个“未初始化的对象”进行操作,于是乎在这里,volatile也起到了解决指令重排序的问题。接下来就是正确的完整代码:
 

class SingletonLazy{private static volatile SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {synchronized (locker) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}
}
public class test3 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);// SingletonLazy s3 = new SingletonLazy();}
}

大功告成!

3.小结

今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,你的支持就是对我最大的鼓励,大家加油!

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

相关文章:

  • 企业网站seo排名360企业自助建站
  • wordpress会务网站模版苏州的互联网公司有哪些
  • 哈 做网站网站建设保密协议书
  • tp5第二季企业网站开发auth权限认证深圳网站网络建设
  • 石家庄建设网站公司简介网站开发公司取名
  • app软件下载网站源码官方网站建设费用
  • 深圳网站建设服务代码建材营销型的网站
  • 十大搞笑素材网站商城网站建设那家好
  • tp框架做视频网站wordpress 视频采集
  • 济源网站建设价格怎么设计平台
  • 网站规划的类型电脑设计长春什么公司比较好
  • 公司网站建设管理办法外贸汽车配件做那个网站
  • 网站 名词解释百姓装潢上海门店具体地址
  • 网站 授权书中国大工程建设需要什么样的人才
  • 建网站公司浩森宇特微信信公众号平台
  • 个人信息网站建设的心得体会淘宝客导购网站怎么建设
  • 好的网站开发自学网站在wordpress文章开头
  • 如何绑定网站域名深圳财务小公司网站
  • 莱芜十七中网站集团公司网站建设方案
  • 12306网站谁做的手把手教你如何建立自己的网站
  • php+html转+wordpress织梦网站优化教程
  • 网站策划案内容网络游戏吧
  • 企业网站主页设计图片画册设计多少钱一页
  • 做一个静态网站要多少钱建网站昆明
  • 网站设计模板图片商品定制首页
  • 做推广的网站名称做seo时网站发文目的
  • 私人定制哪个网站做的比较好建设工程信息网查询平台
  • 沈阳旅游集团网站建设js做网站跳转
  • 哪里做网站seo茂易网站建设
  • 免费空间可以上传网站吗网站维护服务费