什么是线程安全性?如何线程安全

Posted jtrun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是线程安全性?如何线程安全相关的知识,希望对你有一定的参考价值。

什么是线程安全?

 --当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的协同或者同步,这个类都能表现出正确的行为,那么这个类是线程安全的。

 

技术图片

 

 

 1.原子性(Aumic包)

AutomicXXX类  :采用CAS机,即 unsafe.compare.AndSwapInt

public class AtomicControllerTest 


    public static int clientTotal = 5000; //请求总数

    public static int threadTotal = 200;  //同时并发的线程数

    public static AtomicInteger count =  new AtomicInteger(0);

    public static void main(String[] args) throws Exception 
        ExecutorService executorService = Executors.newCachedThreadPool();  //创建线程池
        final Semaphore semaphore = new Semaphore(threadTotal);  //信号量(线程数)
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);  //计数器  (把请求计数)
        for(int i =0 ; i < clientTotal ; i++)
            executorService.execute(()->
                try 
                    semaphore.acquire();//信号量  判断进程是否执行
                    add();
                    semaphore.release();
                 catch (Exception e)
                    log.error("Exception:", e);
                
                countDownLatch.countDown();
            );
        
        countDownLatch.await();  //当所有请求结束
        executorService.shutdown();
        log.info("count:", count);

    
    private static void add()
        count.incrementAndGet();
    

上面的代码是线程安全的,要是将AutomicInteger换成Integer,add方法也随着变成count++,线程将是不安全的。

incrementAndGet中的getAndAddInt是最为关键的代码。

技术图片

 

技术图片

var1 是传进来的对象(count), 如果执行3+1操作,var2是当前的值3,var 4 是1

var5 是底层的值,也就是从主内存拿到的值,由于其他线程也在进行操作,工作内存一直想主内存进行存取操作,主内存的值一直在变化

CAS操作的核心也就是保证从主内存拿到的值,是预期获得的值并没有被其他线程更改,所以在上面的方法中,一直在进行while操作,比较var2 和 var 5,

一旦相同,才会进行相加的操作。主内存和工作内存的运行图如下:

技术图片

 

 

 

synchronized:
1.修饰方法和修饰一个类时,效果一致,先获得资源的线程,先运行,其余访问改资源的线程需要等待
package com.justrun.threadthink.sync;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class SynchronizedExample2 

    // 修饰一个类
    public static void test1(int j) 
        synchronized (SynchronizedExample2.class) 
            for (int i = 0; i < 10; i++) 
                log.info("test1  - ", j, i);
            
        
    

    // 修饰一个静态方法
    public static synchronized void test2(int j) 
        for (int i = 0; i < 10; i++) 
            log.info("test2  - ", j, i);
        
    

    public static void main(String[] args) 
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> 
            example1.test2(1);
        );
        executorService.execute(() -> 
            example2.test2(2);
        );
    

11:42:20.273 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 0
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 1
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 2
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 3
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 4
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 5
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 6
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 7
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 8
11:42:20.298 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 1 - 9
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 0
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 1
11:42:20.298 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 2
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.syn

package com.justrun.threadthink.sync;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class SynchronizedExample1 

    // 修饰一个代码块
    public void test1(int j) 
        synchronized (this) 
            for (int i = 0; i < 10; i++) 
                log.info("test1  - ", j, i);
            
        
        log.info("test-----", j);
    

    // 修饰一个方法
    public synchronized void test2(int j) 
        for (int i = 0; i < 10; i++) 
            log.info("test2  - ", j, i);
        
    

    public static void main(String[] args) 
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> 
            example1.test1(1);
        );
        executorService.execute(() -> 
            example2.test1(2);
        );
    

 

c.SynchronizedExample2 - test2 2 - 3
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 4
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 5
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 6
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 7
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 8
11:42:20.299 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample2 - test2 2 - 9

Process finished with exit code 0

 

2。syhcnhronized修饰一个代码块和修饰一个方法,其原理大致一致,如果代码块修饰的整个方法的内容,那它们是一致的

随机调度资源,每个线程都有机会得到资源
package com.justrun.threadthink.sync;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public class SynchronizedExample1 

    // 修饰一个代码块
    public void test1(int j) 
        synchronized (this) 
            for (int i = 0; i < 10; i++) 
                log.info("test1  - ", j, i);
            
        
        log.info("test-----", j);
    

    // 修饰一个方法
    public synchronized void test2(int j) 
        for (int i = 0; i < 10; i++) 
            log.info("test2  - ", j, i);
        
    

    public static void main(String[] args) 
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> 
            example1.test1(1);
        );
        executorService.execute(() -> 
            example2.test1(2);
        );
    

11:52:20.619 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 0
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 1
11:52:20.619 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 0
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 2
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 1
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 3
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 2
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 4
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 3
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 5
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 6
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 7
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 8
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 1 - 9
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 4
11:52:20.647 [pool-1-thread-1] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test-----
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 5
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 6
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 7
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 8
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test1 2 - 9
11:52:20.647 [pool-1-thread-2] INFO com.justrun.threadthink.sync.SynchronizedExample1 - test-----



以上是关于什么是线程安全性?如何线程安全的主要内容,如果未能解决你的问题,请参考以下文章

为什么会有多线程?什么是线程安全?如何保证线程安全?(带详细例子)

Java线程安全和非线程安全

什么是线程安全,实现线程安全都有哪些方法

什么是线程安全性?

多线程初阶——线程安全

Java(高阶)——线程安全