java并发之synchronized详细分析(全)

前言

本篇博文结合的代码涉及以下知识,可查看我之前的文章
java之Thread类详细分析(全)
【操作系统】线程与进程的深入剖析(全)

造成线程安全主要是数据共享,为了解决这种情况
引出synchronized 是 Java 中的关键字,是一种同步锁(对方法或者代码块中存在共享数据的操作)。同步锁可以是任意对象

具体修饰的对象有3种方式

  1. 修饰代码块
  2. 修饰方法
  3. 修饰静态方法
  • 被修饰的代码块称为同步语句块,作用的范围是大括号{ }内的内容
  • 修饰的是方法,其作用范围为整个方法,作用对象是调用该代码块的对象(虽然可以修饰方法,但 synchronized 并不属于方法定义的一部分,因此,synchronized 关键字不能被继承)
  • 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象(修饰一个类也同理)

1. 实例方法

  • 实例方法不是静态方法

  • 如果在父类中的某个方法使用了 synchronized 关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized 关键字才可以。或者在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步

一个线程对应一个对象 synchronized 实例方法,其他线程不能对该对象的其他 synchronized 方法访问(一个对象只有一把锁)。大概意思是当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,但其他线程可以访问该对象的其他非synchronized方法两个不同对象访问不同个东西,不会产生歧义,但是如果同一个对象访问同一个东西会有歧义而且不加锁

两个线程中同一个对象访问,加了锁机制不会出错

public class aa implements Runnable{{
    //共享资源(临界资源)
    static int i=0;

    /**
     * synchronized 修饰实例方法
     */
    public synchronized void add(){
        i++;
    }
    
    @Override
    public void run() {
        for(int j=0;j<10;j++){
            add();
        }
    }
    public static void main(String[] args)
    {
    	aa b=new aa();
        Thread m1=new Thread(b);
        Thread m2=new Thread(b);
        m1.start();
        m2.start();
        m1.join();
        m2.join();
        System.out.println(i);
    }
}

但如果两个线程两个不同对象访问,加了锁的机制还是会出错

aa b=new aa();
aa c=new aa();
Thread m1=new Thread(b);
Thread m2=new Thread(c);

需要将其方法添加为静态方法即可
因为两个不同对象,访问同一个锁会出错,定义为静态方法,即当前类的对象锁,不会出现歧义对象

2. 静态方法

对静态方法加锁,锁是当前类的class对象锁

public static synchronized void add(){
        i++;
    }

3. 代码块

class Ticket {
    //票数
    private int number = 30;
    //操作方法:卖票
    public synchronized void sale() {
        //判断:是否有票
        if(number > 0) {
            System.out.println(Thread.currentThread().getName()+" : 
                    "+(number--)+" "+number);
        }
    } }

如果一个代码块被 synchronized 修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

  • 1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
  • 2)线程执行发生异常,此时 JVM 会让线程自动释放锁。

那么如果这个获取锁的线程由于要等待 IO 或者其他原因(比如调用 sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,但一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过 Lock 就可以办到

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值