线程安全的七种解决方法

本文探讨了多线程编程中的线程安全问题及其解决方法,包括原子性、可见性和有序性的概念。介绍了同步代码块、同步方法、Lock类、volatile、ThreadLocal、阻塞队列(LinkedBlockingQueue)和Atomic类作为线程安全的解决方案,并详细讲解了Lock和synchronized的区别。特别提到了CAS的ABA问题及其解决策略。
摘要由CSDN通过智能技术生成

一、前言

多线程的知识点真多啊,整理了一下,发现还是有很多不懂的地方(脑子有点乱,乱的我都不知道都是哪里不懂了),印象最深的,CAS的ABA问题解决方案中:

这个方法:

compareAndSet(预期值,更新值,预期时间戳,更新时间戳)

是在预期值等于更新值的时候返回true吧

那,

代码示例中:

n = new AtomicStampedReference<Integer>(0,0);
Thread t1 = new Thread(){
   
   public void run(){
   
      for(int i=0; i<1000; i++){
   
         int stamp;
         Integer reference;
         do{
   
             stamp = n.getStamp();
             reference = n.getReference();
         } while(!n.compareAndSet(reference, reference+1, stamp, stamp+1));
      }
   }
};

reference和reference+1啥时候能相同啊

目前不懂,希望大佬能够不吝赐教

二、线程安全

(一)多线程特性

多线程编程要保证满足三个特性:原子性、可见性、有序性。

1)原子性

原子性,即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

2)可见性

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。显然,对于单线程来说,可见性问题是不存在的。

3)有序性

程序执行的顺序按照代码的先后顺序执行。

(二)线程安全的定义

如果有多个线程同时运行同一个实现了Runnable接口的类,程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的;反之,则是线程不安全的。

(三)线程安全问题产生的原因

  • 多个线程在操作共享数据
  • 操作共享数据的线程代码有多条
  • 多个线程对共享数据有写操作

(四)解决方法——线程同步

Java中7种线程同步机制

  1. 同步代码块(synchronized)
  2. 同步方法(synchronized)
  3. Lock类
  4. 特属域变量(volatile)
  5. 局部变量(ThreadLocal)
  6. 阻塞队列(LinkedBlockingQueue)
  7. 原子变量(Atomic)
1、同步代码块(synchronized)

synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

语法:

synchronized(同步锁){
   
     需要同步操作的代码
}

同步锁:对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁

  • 锁对象可以是任意类型。
  • 多个线程要使用同一把锁。

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。

示例代码:

package com.multithread.thread;

public class Ticket implements Runnable {
   
    private int ticktNum = 100;

    //定义锁对象,可以是任意类型,但一定要是对象
    Object obj = new Object();

    public void run() {
   
        while(true){
   
            synchronized (obj){
   //同步代码块
                if(ticktNum > 0){
   
                    //1.模拟出票时间
                    try {
   
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                    //2.打印进程号和票号,票数减1
                    String name = Thread.currentThread().getName();
                    System.out.println("线程"+name+"售票:"+ticktNum--);
                }
            }
        }
    }
}
2、同步方法(synchronized)

使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

语法:

public synchronized void method(){
   
   //可能会产生线程安全问题的代码 
}

那么,这种方式没有了自定义的同步锁,锁是谁呢?

  • 对于非static方法,同步锁就是this(就是调用同步方法的类对象)
  • 对于static方法,同步锁是当前方法所在类的字节码对象(类名.class)

示例代码:

package com.multithread.thread;

public class Ticket implements Runnable {
   
    private int ticktNum = 100;

    //定义锁对象
  //  Object obj = new Object();这种方式不需要自定义同步锁

    public void run() {
   
        while(true){
   
            sellTicket();
        }
    }

    private synchronized void sellTicket(){
   //同步方法
        if(ticktNum > 0){
   
            //1.模拟出票时间
            try {
   
                Thread.sleep(100);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
            //2.打印进程号和票号,票数减1
            String name = Thread.currentThread().getName();
            System.out.println("线程"+name+"售票:"+ticktNum--);
        }
    }
}
3、Lock类

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

3.1Lock接口关系图

在这里插入图片描述

  • Lock和ReadWriteLock是两大锁的根接口

  • Lock 接口支持重入、公平等的锁规则:实现类 ReentrantLock、ReadLock和WriteLock。
    ReadWriteLock 接口定义读取者共享而写入者独占的锁,实现类:ReentrantReadWriteLock。

3.2Lock类同步锁方法:
public void lock() :加同步锁。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值