多线程——线程安全

前言

  小咸儿在上一篇多线程——线程通讯中,提到线程安全问题,今天就来说一说。


叙述

宝图

  先来一张导图来看看线程安全的分布?
在这里插入图片描述

线程安全

  线程安全是什么呢?

  当多个线程共享同一个全局变量,做写操作时,可能会收到其他线程的干扰,做读操作则不会发生线程安全。

解决方法

  既然遇到了线程安全问题,那么该如何解决这个问题呢?

这时候就会先考虑到两个关键字:volatilesynchronized

volatile关键字: 保证可见性的问题。保证另一个线程可见,及时将修改后的变量刷新到主内存中。虽然能够保证可见性,但是无法保证原子性。

代码展示:

package com.practice.demo.thread.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLock {
    private volatile Map<String, String> caChe = new HashMap<>();
    /**
     * 读写锁
     */
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    /**
     * 写入锁
     */
    private ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
    private ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();

    /**
     * 写入元素
     * @param key  key值
     * @param value  value值
     */
    public void put(String key, String value){
        try {
            writeLock.lock();
            System.out.println("写入put方法key:" + key + ",value:" + value + ".开始");
            Thread.currentThread().sleep(100);
            caChe.put(key,value);
            System.out.println("写入put方法key:" + key + ",value:" + value + ".结束");
        } catch (Exception e){

        }finally {
            writeLock.unlock();
        }
    }
    /**
     * 读取元素
     * @param key  key值
     *
     */
    public String get(String key){
        try {
            readLock.lock();
            System.out.println("读取key:" + key + ".开始");
            Thread.currentThread().sleep(100);
            String value = caChe.get(key);
            System.out.println("读取key:" + key + ".结束");
            return value;
        }catch (Exception e){
            return null;
        }finally {
            readLock.unlock();
        }
    }

    public static void main(String[] args){
        ReadWriteLock readWriteLock = new ReadWriteLock();
        // 写线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++){
                    readWriteLock.put("i", i + "");
                }
            }
        });
        // 读线程
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++){
                    readWriteLock.get(i + "");
                }
            }
        });
        t1.start();
        t2.start();
    }
}


打印结果:
在这里插入图片描述

  结果: 即当这个线程开始和结束都执行完之后,另一个线程才能进行,并且全局变量发生变化后对另一个线程可见。

synchronized关键字: 内置锁,保证线程原子性,当线程进入方法的时候,自动获取锁,一旦锁被某个线程获取到后,其他的线程就会等待。即可修饰代码块也可用来修饰方法。

代码展示

public void run() {
        // 使用account作为同步监视器,任何线程进入下面同步代码块之前
        // 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改她
        // 这种做法符合:“加锁——修改——释放锁”的逻辑
        synchronized (account) {
            // 账户余额大于取钱数目
            if (account.getBalance() >= drawAmount) {
                // 吐出钞票
                System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount);
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                // 修改余额
                account.setBalance(account.getBalance() - drawAmount);
                System.out.println("\t余额为:" + account.getBalance());
            } else {
                System.out.println(getName() + "取钱失败,余额不足!");
            }
        }
    }

区别

  volatile和synchronized的区别:

  • volatile不会进行加锁操作:
      volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会执行线程阻塞,因此volatile变量是一种比synchronized关键字更加轻量级的同步机制。

  • volatile不如synchronized安全:
      在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更加难以理解。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它。一般来说,使用同步机制会更安全。

  • volatile变量作用类似于同步变量读写操作:
      从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。

  • volatile无法同时保证内存可见性和原子性:
      加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性,原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count++”,“count = count + 1”。

问题

  解决线程安全的问题不仅只有这两种处理办法,还有一种锁(Lock),具体且听小咸儿下回分享。


总结

  多线程是十分实用并且常用的内容,接下来小咸儿还会继续深入学习多线程,更多的内容等待更新。

感谢您的阅读~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值