一起走进多线程(五)

多线程(五)

关于多线程基础部分就主要在前面四节讲解了,如果还有小伙伴有疑问或者笔者梳理的有问题的话,欢迎在下方评论区指正,大家一起学习共同进步。结束了基础知识,我们进入到并发工具和锁的学习。

线程间的共享

多线程完成任务必然会存在一个数据共享的过程,而这个过程我们需要采取措施来避免出现错误。

1.首先讲一下synchronized内置锁

​ 每个线程如果单独的运行,相互之间不进行协作,那他们产生的结果也不会有太大的价值,如果线程之间能过共同完成任务,那这个结果也会十分有价值,而共同完成任务的同时需要保证数据的准确性,毕竟如果结果不对,那做再多的工作也将是无用功。java程序本身就是支持多线程的,当多个线程同时访问一个对象时,我们可以加上synchronized关键字修饰方法或者同步代码块,它能够保证同一时刻,只有一个线程可以进入方法或者代码块内。

​ 现在我们用代码演示下在多线程的情况下synchronized关键字使用前和使用后的情况。

public static void main(String[] args) {
        Util util = new Util();
        SelfThread3 selfThread3 = new SelfThread3(util);
        for (int i =0;i<5;i++){
            new Thread(selfThread3).start();
        }
        Thread.sleep(10000);
    }

//线程
class SelfThread3 implements Runnable{
    private Util util;
    public SelfThread3(Util util){
        this.util = util;
    }

    public void run(){
        try {
            util.job();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//业务工具类
class Util{
    private int add1;
    public void  job() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"--------"+"线程开始处理业务");
        Thread.sleep(2000);//模拟业务处理
        System.out.println(Thread.currentThread().getName()+"--------"+"线程结束处理业务");
    }
//    public synchronized void  job() throws InterruptedException {
//        System.out.println(Thread.currentThread().getName()+"--------"+"线程开始处理业务");
//        Thread.sleep(2000);
//        System.out.println(Thread.currentThread().getName()+"--------"+"线程结束处理业务");
//    }
}

首先演示了在业务处理方法不加synchronized关键字的情况:
在这里插入图片描述在这里插入图片描述不加synchronized关键字所有的线程同时进入方法进行业务操作,并同时结束。 接下来我们看看在方法上加synchronized关键字的效果:
在这里插入图片描述
在这里插入图片描述每个线程都需要去获取锁才能进入方法体内,如果锁被其他线程占有,则需要等它释放锁后才能去争抢锁。这也就体现了排他性。

这里还有一个对象锁和类锁的概念,对象锁是指synchronized用于实例方法上,类锁是用于类的静态方法上。一个类可以有多个对象实例,每个对象实例都是一个对象锁,他们互不干扰。类可以有多个对象锁,但是只有一个类锁,因为类锁的实质是指每个类对应的class对象。类锁和对象锁之间也是互不干扰的。

2.volatile

volatile可以保证线程之间对变量的操作后的可见性(这里需要了解下多线程的内存模型,只做简单的介绍,后续作内存模型专门讲解时在详细介绍)
在这里插入图片描述当线程A对变量a进行操作后,并且更新到主存中,而这时候线程b并没有及时从主存中更新a到本地内存中,那么就会使用旧值而导致出错。而如果使用volatile关键字修饰变量a,那么线程A修改变量a并更新到主存中,线程b会去主存中获取变量a的最新值。这也就是volatile关键字保证内存可见性,他比synchronized关键字要更加轻量级,但是它不能保证原子性。比较适合多个线程读,一个线程写的场景。另外volatile还还有一个特性禁止指令的重排序。

接下来用代码演示下volatile的可见性和不能保证原子性。

public class VolatileTest {
    private static boolean flag;
//    private volatile static boolean flag;
    private static int num;
    
    public static class Thread1 extends Thread{
        @Override
        public void run() {
            System.out.println("start!!!!!!!!!!!");
            while (!flag);
            System.out.println("num="+num);
        }
    }
    public static void main(String[] args) {
        new thread1().start();
        Thread.sleep(1000);
        flag = true;
        num = 199;
        Thread.sleep(3000);
        System.out.println("main end*******************");
    }
}

我们先来看看当一个线程改变不带volatile关键字变量的值,另一个线程是否能获取到新值?
在这里插入图片描述可以发现在不使用volatile关键字修饰时,无法即时获得到新的flag值,下面我们用volatile关键字修饰flag变量,看看结果如何。在这里插入图片描述在添加了volatile关键字后,可以立即获得到最新的值,这就体现了volatile的可见性。那volatile是否能保证原子性呢?接下来我们也一起验证一下。
在这里插入图片描述可以看的出来这时没有对变量添加volatile关键字修饰,而是使用了synchronized锁,结果是正确的。那么,如果我们使用volatile关键字不用synchronized关键字呢?

在这里插入图片描述可以看到volatile关键字修饰的时候并不能得到正确结果,也就证明了volatile关键字无法保证操作的原子性。volatile还有禁止指令重排序的功能。

本节主要对synchronized和volatile进行了讲解。下一节主要会讲解ThreadLocal的相关知识。

以上就是本节内容,谢谢大家的阅读,如有错漏,欢迎评论区指正提出😚!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值