架构系列——你用的线程是安全的吗?怎么解决?

简介: 架构系列——你用的线程是安全的吗?怎么解决?

前言

线程安全一般是多线程的安全,首先可以了解一些知识点:

架构系列——进程与线程的关系探索

架构系列——并发、并行与多线程关系探索

一、什么是线程安全

当多个线程访问一个类(对象或者方法),被访问者始终都能表现出正确的行为,那么这个类(对象或者方法)就是线程安全的。

二、保证线程安全的三个特性

1.原子性

提供互斥访问,同一时刻只能有一个线程对数据进行操作(atomic,synchronized);


synchronized修饰的对象有四种:


(1)修饰代码块,作用于调用的对象;


(2)修饰方法,作用于调用的对象;


(3)修饰静态方法,作用于所有对象;


(4)修饰类,作用于所有对象。

2.可见性

一个线程对主内存的修改可以及时地被其他线程看到(volatile);

3.有序性一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则)。


1.程序次序规则:在一个单独的线程中,按照程序代码书写的顺序执行。


2.锁定规则:一个unlock操作happen—before后面对同一个锁的lock操作。


3.volatile变量规则:对一个volatile变量的写操作happen—before后面对该变量的读操作。


4.线程启动规则:Thread对象的start()方法happen—before此线程的每一个动作。


5.线程终止规则:线程的所有操作都happen—before对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。


6.线程中断规则:对线程interrupt()方法的调用happen—before发生于被中断线程的代码检测到中断时事件的发生。


7.对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before它的finalize()方法的开始。


8.传递性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。

三、线程安全举例

设计Thread类的子类,总共5个数,每启动一个线程,数量就减一。

package com.han;
public class MyThread extends Thread{
  private int count = 5;
  @Override
    public void run() {
        count--;
        System.out.println(this.currentThread().getName() + " count = " + count);
    }
  public static void main(String[] args) {
    MyThread myThread = new MyThread();
    Thread t1 = new Thread(myThread, "t1");
    Thread t2 = new Thread(myThread, "t2");
    Thread t3 = new Thread(myThread, "t3");
    Thread t4 = new Thread(myThread, "t4");
    Thread t5 = new Thread(myThread, "t5");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}

四、出现问题

运行上面的代码出现类似下面的结果:

t1 count = 3
t3 count = 2
t2 count = 3
t4 count = 1
t5 count = 0

有两个问题:

1.剩余数量不正确,本应该是4、3、2、1、0

2.启动顺序不正确,本应该是t1、t2、t3、t4、t5

四、解决问题

1、剩余数量是由run方法里面计算的,显然有多个线程在同一时间拿到了count并进行计算。使用synchronized修饰run方法是一种比较简单的方式,还可以使用Lock,这样就能在同一时间只有一个线程拿到count


2.这个顺序不是代码的顺序,而是CPU的随机分配,可以使用队列解决


参考文献:

[1].Java中如何保证线程安全性

[2].java线程安全问题以及同步的几种方式


相关文章
|
4月前
|
调度 数据库 uml
高级系统架构设计师问题之线程状态变化如何解决
高级系统架构设计师问题之线程状态变化如何解决
|
22天前
|
Kubernetes 安全 微服务
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
42 8
|
2月前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
3月前
|
存储 监控 安全
大数据架构设计原则:构建高效、可扩展与安全的数据生态系统
【8月更文挑战第23天】大数据架构设计是一个复杂而系统的工程,需要综合考虑业务需求、技术选型、安全合规等多个方面。遵循上述设计原则,可以帮助企业构建出既高效又安全的大数据生态系统,为业务创新和决策支持提供强有力的支撑。随着技术的不断发展和业务需求的不断变化,持续优化和调整大数据架构也将成为一项持续的工作。
|
3月前
|
Kubernetes 安全 微服务
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
在5G电信领域,Kubernetes集群中部署微服务至关重要,但也带来了重大的安全挑战。Istio作为一个强大的开源服务网格,能有效地管理这些微服务间的通信,通过其控制平面自动将Sidecar代理注入到各微服务Pod中,确保了安全且高效的通信。Istio的架构由数据平面和控制平面组成,其中Sidecar代理作为Envoy代理运行在每个Pod中,拦截并管理网络流量。此外,Istio支持多种Kubernetes发行版和服务,如EKS等,不仅增强了安全性,还提高了应用性能和可观测性。
71 0
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
|
3月前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
3月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
3月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
61 1
|
3月前
|
存储 监控 安全
|
3月前
|
Java 测试技术 Android开发
Android项目架构设计问题之构造一个Android中的线程池如何解决
Android项目架构设计问题之构造一个Android中的线程池如何解决
25 0

热门文章

最新文章