多线程安全(synchronized、三大特性、生命周期以及优缺点)

一、线程安全

一个对象是否安全取决于它是否被多个线程访问(访问是访问对象的方式)。要使对象线程安全,name需要采用同步的机制来协同对对象可变状态的访问。(java这边采用synchronized,其他还有volatile类型的变量,显式锁以及原子变量)

当某个多线程访问同一个可变状态时候没有同步,则会出现错误,解决方法:

1、不在线程之间共享该变量

2、将该变量修改为不可变变量

3、访问状态时候使用同步

安全性的解释:当多线程访问某个类时,这个类始终能表现出正确的行为(不管运行时采用何种跳读方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同),那么这个类是安全的。

  • 无状态对象一定是线程安全的。

  • 在实际情况下,尽可能使用户现有的线程安全对象,比如说用vector 而不是 ArrayList。

要想并发程序正确地执行,必须要保证“原子性”,“可见性”和"有序性"。只要有一个没有被保证,就有可能导致程序运行不正确。

二、并发三大特性

先说说并发,并发是指 在操作系统中,一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,担任一个时刻点上只有一个程序在处理机上运行。

1、原子性

原子性的意思代表着————“不可分割”,很多操作都是非原子性。

a.竞态条件

当某个计算的正确性取决于多个线程的交替执行顺序时,那么就会发生竞态条件,也就是说,正确的结果取决于运气。竞态条件和原子性相关,或者说,之所以代码会发生竞态条件,就是因为代码不是以院子方式操作的,而是一种复合操作。

在多线程没有同步的情况下,多种操作序列的执行时序发生变化导致错误(是指设备或系统出现不恰当的执行时序,而得到不正确的结果)。即类型为先检查后执行操作。


Class NotSafeThread{
    int i = 0;
    public void Nst(){
        if(i==0){
            //这边就会出现竞态,如果两个线程AB都运行这边,A判断 i == 0, i++操作 
            //那么i为1, 但是B线程也在A判断的时候判断为True, 又进行一次 i++, i=2了
            i++;
        }
    }
}

b.复合操作

我们将 “先检查后执行” 和 “读取+修改+写入” 等操作的院子形式成为复合操作:包含一组必须以院子方式执行的操作以确保线程安全。

c.原子操作(atomic operations)

原子操作指的是在一步之内就完成而且不能被中断

//比如 多线程中 int i = 0; i++; 多个线程的时候会出现问题。
++count 看起来是一个操作,但这个操作并非原子性的,因为它可以被分成三个独立的步骤:①读取count的值 ②值+1 ③将计算结果写入count 这是一个“读取-修改-写入” 的操作序列,并且结果状态依赖于之前的状态。


public void service(ServletRequest req,ServletResponse resq){
    BigInteger i = extractFromRequest(req);
    BigInteger[] factor= factor(i);
    ++count;
    encodeIntoResponse(resp,factors);

}
 x = 10;        //语句1 
 y = x;         //语句2
 x++;           //语句3
 x = x + 1;     //语句4

只有语句1是原子性操作,其他三个语句都不是原子性操作。

语句1是直接将数值10赋值给 x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。

语句2实际上包含2个操作,它先要去读取x的值,在将x的值写入工作内存,虽然读取 x 的值以及将 x 的值写入到工作内存,这两个操作都是原子性操作,但是合起来就不是原子性操作了。

同样的,x++ 和 x = x + 1 包括三个操作: 读取 x 的值,进行 +1 操作,写入新的值。

也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。

 

 

2、可见性

synchronized 关键值,开始时会从内存中读取,结束时会将变化刷新到内存中,所以是可见的。

volatile 关键值,通过添加lock指令,也是可见的。

可见性

  • 多线程环境下,一个线程对于某个共享变量的更新,后续访问该变量的线程可能无法立即读取到这个更新的结果,这就是不可兼得情 况。

  • 可见性就是指一个线程对共享变量的更新的结果对于读取相应共享变量的线程而言是否可见的问题。

  • 可见性和原子性的联系和区别:

    • 原子性描述的是一个线程对共享变量的更新,从另一个线程的角度来看,它要么完成,要么尚未发生。

    • 可见性描述一个线程对共享变量的更新对于另一个线程而言是否可见。


//普通情况下,多线程不能保证可见性

bool stop = false;

new Thread(() -> { 
    System.out.println("Ordinary A is running..."); 
    while (!stop) ;
    System.out.println("Ordinary A is terminated."); 
}).start(); 

Thread.sleep(10); new Thread(() -> { 
    System.out.println("Ordinary B is running..."); 
    stop = true; 
    System.out.println("Ordinary B is terminated."); 
}).start();

某次运行结果: Ordinary A is running... Ordinary B is running... Ordinary B is terminated.

猜你喜欢

转载自www.cnblogs.com/nineberg/p/12274106.html