6、并发安全

本文探讨了线程安全的概念,提供了实现线程安全的多种策略,如栈封闭、无状态、不可变对象、volatile、加锁和CAS、线程局部变量等。通过示例分析了死锁问题,并给出了避免死锁的建议,强调了并发编程中性能和正确性的平衡。同时,介绍了线程安全的单例模式实现。
摘要由CSDN通过智能技术生成

类的线程安全定义,怎么才能做到类的线程安全?
栈封闭
变量在方法内部申明;这些变量都处于栈封闭;栈是线程私有的(安全);

无状态
类内部无任何成员变量;

让类不可变
让状态不可变 所有包装类都不可变 ;

1、对于一个类来讲,所有的成员变量应该是私有的,同样的只要有可能, 所有的成员变量应该加 final 关键字;

2、不提供任何可修改成员变量的方法,同时,成员变量也不做为方法的返回值;

Akka 框架

volatile
保证类的可见性;最适合一个线程写,多个线程读的情景;( ConcurrentHashMap )

加锁和CAS
安全的发布

public class ImmutableTwo {//不可修改  不提供可以修改的方法 但仍然  加 final  最安全
    private List<Integer> list = new ArrayList<>(3);public ImmutableTwo() {
        list.add(0);
        list.add(1);
    }public boolean isContains(int i) {
        return list.contains(i);
    }
        //不安全的发布
    // 线程不安全  返回了对象的引用。
    // 可返回一个深拷贝的副本
    // 或者转换成一个线程安全的容器返回
    public List<Integer> getList() {
        return list;
    }}



ThreadLocal
Servlet
不是线程安全的类,

1、很少有共享的需求;

2、web容器接收到请求,返回应答 都是由一个线程来负责的;

如果数据要在多个响应之间共享,要注意线程安全的问题;

类的线程安全定义
如果在多线程环境下使用一个类,不管多线程如何使用或者调度这个类,这个类总是表现出正确的行为

这个类就是线程安全的;

操作的原子性

内存的可见性

一个类在多个线程之间共享状态的时候,就会出现线程不安全;

线程不安全引发的问题?
死锁:是指两个或两个以上进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,他们都无法推进下去,此时处于死锁状态;

竞争资源多于一个,小于等于线程数量;资源只有一个,只会产生资源竞争;

检查死锁
代码块
jdk bin 目录下


 ~ cd /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk
➜  jdk1.8.0_40.jdk ls
Contents
➜  jdk1.8.0_40.jdk cd Contents
➜  Contents ls
Home       Info.plist MacOS
➜  Contents cd Home
➜  Home ls
COPYRIGHT                          THIRDPARTYLICENSEREADME-JAVAFX.txt db                                 jre                                release
LICENSE                            THIRDPARTYLICENSEREADME.txt        include                            lib                                src.zip
README.html                        bin                                javafx-src.zip                     man
➜  Home cd bin
➜  bin ls
appletviewer   jarsigner      javafxpackager jcmd           jhat           jmc            jstack         keytool        policytool     schemagen      unpack200
extcheck       java           javah          jconsole       jinfo          jps            jstat          native2ascii   rmic           serialver      wsgen
idlj           javac          javap          jdb            jjs            jrunscript     jstatd         orbd           rmid           servertool     wsimport
jar            javadoc        javapackager   jdeps          jmap           jsadebugd      jvisualvm      pack200        rmiregistry    tnameserv      xjc
➜  bin jps
88320 Jps
391
65373 Launcher
➜  bin
➜  bin
➜  bin jps
88343 Jps
391
88331 DeadLockSample
88332 Launcher
➜  bin
➜  bin jstack 88331
2021-01-19 18:22:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.40-b25 mixed mode):"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fe76c841000 nid=0x460b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"Thread-1" #12 prio=5 os_prio=31 tid=0x00007fe77101a800 nid=0xa403 waiting for monitor entry [0x00007000058d9000]
   java.lang.Thread.State: BLOCKED (on object monitor)
  at com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock.run(DeadLockSample.java:28)
  - waiting to lock <0x000000076adb5a08> (a java.lang.String)
  - locked <0x000000076adb5a40> (a java.lang.String)"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fe771838800 nid=0xa503 waiting for monitor entry [0x00007000057d6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
  at com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock.run(DeadLockSample.java:28)
  - waiting to lock <0x000000076adb5a40> (a java.lang.String)
  - locked <0x000000076adb5a08> (a java.lang.String)"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007fe76e835000 nid=0xa903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007fe76e821800 nid=0x5503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"C2 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fe771815000 nid=0x3e03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fe76f00b000 nid=0x3f03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fe76e07f000 nid=0x3b03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fe76d823000 nid=0x4103 runnable [0x00007000050c1000]
   java.lang.Thread.State: RUNNABLE
  at java.net.SocketInputStream.socketRead0(Native Method)
  at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
  at java.net.SocketInputStream.read(SocketInputStream.java:170)
  at java.net.SocketInputStream.read(SocketInputStream.java:141)
  at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
  at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
  at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
  - locked <0x000000076af13018> (a java.io.InputStreamReader)
  at java.io.InputStreamReader.read(InputStreamReader.java:184)
  at java.io.BufferedReader.fill(BufferedReader.java:161)
  at java.io.BufferedReader.readLine(BufferedReader.java:324)
  - locked <0x000000076af13018> (a java.io.InputStreamReader)
  at java.io.BufferedReader.readLine(BufferedReader.java:389)
  at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fe76e817800 nid=0x3803 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
​
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fe76c80b000 nid=0x3203 in Object.wait() [0x0000700004db5000]
   java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)
  - waiting on <0x000000076ab06f58> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
  - locked <0x000000076ab06f58> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
  at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fe76c80a800 nid=0x3103 in Object.wait() [0x0000700004cb2000]
   java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)
  - waiting on <0x000000076ab06998> (a java.lang.ref.Reference$Lock)
  at java.lang.Object.wait(Object.java:502)
  at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
  - locked <0x000000076ab06998> (a java.lang.ref.Reference$Lock)"main" #1 prio=5 os_prio=31 tid=0x00007fe76d808800 nid=0xe03 in Object.wait() [0x000070000408e000]
   java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)
  - waiting on <0x000000076adb5a90> (a com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock)
  at java.lang.Thread.join(Thread.java:1245)
  - locked <0x000000076adb5a90> (a com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock)
  at java.lang.Thread.join(Thread.java:1319)
  at com.xiangxue.xiancheng.dielock.DeadLockSample.main(DeadLockSample.java:44)"VM Thread" os_prio=31 tid=0x00007fe771814000 nid=0x4c03 runnable
​
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fe76e811000 nid=0x1a07 runnable
​
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fe770809000 nid=0x1e03 runnable
​
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fe770809800 nid=0x1c03 runnable
​
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fe77080a000 nid=0x2a03 runnable
​
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fe76e812000 nid=0x5303 runnable
​
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fe76c809000 nid=0x5203 runnable
​
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fe76c80a000 nid=0x2d03 runnable
​
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fe771808800 nid=0x5103 runnable
​
"GC task thread#8 (ParallelGC)" os_prio=31 tid=0x00007fe771809000 nid=0x2f03 runnable
​
"GC task thread#9 (ParallelGC)" os_prio=31 tid=0x00007fe76f809000 nid=0x4e03 runnable
​
"VM Periodic Task Thread" os_prio=31 tid=0x00007fe76e836000 nid=0xa703 waiting on condition
​
JNI global references: 22
​
​
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007fe76e8378a8 (object 0x000000076adb5a08, a java.lang.String),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fe77500d8a8 (object 0x000000076adb5a40, a java.lang.String),
  which is held by "Thread-1"
​
Java stack information for the threads listed above:
===================================================
"Thread-1":
  at com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock.run(DeadLockSample.java:28)
  - waiting to lock <0x000000076adb5a08> (a java.lang.String)
  - locked <0x000000076adb5a40> (a java.lang.String)
"Thread-0":
  at com.xiangxue.xiancheng.dielock.DeadLockSample$DieLock.run(DeadLockSample.java:28)
  - waiting to lock <0x000000076adb5a40> (a java.lang.String)
  - locked <0x000000076adb5a08> (a java.lang.String)
​
Found 1 deadlock.
​
➜  bin

避免死锁:
1、指定获取锁顺序;

//hash码值
// 获取唯一 不变的 hashCode   冲突概率:千万分之一
System.identityHashCode()

性能和思考
使用并发的目标是为了提高性能。引入多线程后,引入了额外的开销;

衡量应用程序的性能:服务时间、延迟时间(处理速度)、吞吐量(处理能力的指标)、可伸缩性(随着计算机资源的增加处理能力是否线性增加);

一般来说,处理能力比处理速度更受重视

1、先保证程序正确,再提高速度(达不到要求的时候)。

一个应用程序里,串行部分是无法避免的,

amdahl定律( 阿姆达尔定律 ):
在并行计算中,使用多个处理器的程序的加速比受限制于程序串行部分的执行时间。例如,如果一个程序使用一个CPU核执行需要20小时,其中部分代码只能串行,需要执行1个小时,

其他19小时的代码执行可以并行,那么,不考虑有多少CPU可用来并行执行程序,最小执行时间不会小于1小时(串行工作的部分),因此加速比被限制为最多20倍(20/1)。

线程饥饿
低优先级的线程,总是拿不到执行时间;

影响性能的因素
上下文切换

一次切换耗时 : 5000~10000个时钟周期,几微秒

合理分配线程数量

内存同步

加锁:增加额外的指令

阻塞

线程阻塞,被挂起,包括两次额外的上下文切换;

减少锁的竞争
缩小锁的范围

对锁的持有:快进快出;尽量缩短持有锁的时间 (不直接锁方法,锁方法中容易产生并发安全的代码块)

避免多余的缩减锁范围

锁粗化;

如果两锁之间执行代码块时间很短,就设置成一个锁(上锁的时候,jvm插入额外指令会消耗时间)现在JVM 编译的时候自动会优化,但编程要尽量避免,

减小锁的粒度

在使用锁的时候,锁保护的对象是多个,多个对象是独立变化的时候,不如用多个锁来独立保护;(类似于分段锁)

但是要避免发生死锁;

锁分段

ConcurrentHashMap 但是有些方法不是很准确( 比如 size( ) )

替换独占锁

1、CAS操作

2、使用读写锁

3、使用系统提供的并发容器

线程安全的单例模式
双重检查

不安全 ; 但是可以 对 singleDocl 变量加 volatile 来保证安全

private volatile static SingleDoc singleDocl;

public class SingleDoc {
    private static SingleDoc singleDocl;
    public SingleDoc() {
    }
    private SingleDoc getSingleDoc(){
        if (singleDocl == null){
            synchronized (SingleDoc.class){
                if (singleDocl == null){
                    // 构造方法并不是原子操作
                    // new 出来的时候,已经有对象的实例地址(对象引用),
                    // 但是对象的域不一定赋值完成(如果对象内部有多个变量、例如对象变量、数据库查询结果赋值相关)
                    //A 线程赋值过程中, B线程已经拿去使用了
                    singleDocl = new SingleDoc();
                }
            }
        }
        return singleDocl;
    }
}

饿汉式

类初始化模式、枚举模式

在JVM中,对类的加载和初始化,由虚拟机保证线程安全

// 延迟初始化
public class SingleInit {
    private SingleInit() {
    }//定义一个私有类,持有当前类的实例
    // 获取实例的时候,加载当前私有类来获取实例
    private static class InstanceHolder{
        public static SingleInit instance = new SingleInit();
    }public static SingleInit getInstance(){
        return InstanceHolder.instance;
    }}

懒汉式

/**
 * 延迟占位模式
 */
public class InstaceLazy {private Integer value;
    private Integer val;// 可能很大,public InstaceLazy(Integer value) {
        this.value = value;
    }public Integer getValue() {
        return value;
    }private static class VolHolder{
        public static Integer volHolder = new Integer(1000);
    }public Integer getVal() {
        return VolHolder.volHolder;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值