synchronized关键字讲解

目录

1、synchronized 简单介绍

(1)为什么要用 synchronized  

(2)用synchronized 加锁的目的

(3)synchronized 加锁对象

2、synchronized 几种用法

 (1)修饰代码块

(2)修饰普通方法

(3)修饰静态方法

 3、synchronized 特性

(1)互斥

(2)刷新内存

(3)可重入

 在Java中使用 synchronized 关键字对线程加锁 !!

在正式学习 synchronized 关键字之前,我们一定要学会读它(掌握发音)!!!


synchronized百度翻译

1、synchronized 简单介绍

(1)为什么要用 synchronized  

       当两个线程同时对一个变量进行修改时,由于修改可能不是原子操作,就会导致一个一个线程正在操作时,另一个线程突然插入,导致第一个线程修改失败。

       而使用 synchronized 关键字就可以避免这种情况,把 synchronized 作用于该变量。当一个线程对该变量进行修改时,该进行就会对该变量进行加锁,另一个线程再进行操作时,就会出现互斥无法打断该操作。

举个例子:有两个男生A 和 B 同时追一个妹子,当妹子还是单身的时候,她可以接受两个男生的告白。一旦 A 追上了妹子,那 B 就不能给妹子表白,知道 A 和妹子分手。这种情况就相当于对妹子进行上锁 !!!       

(2)用synchronized 加锁的目的

        使用 synchronized 加锁的是让多个线程争一个对象,让线程在执行过程中阻塞等待,从而保证了线程安全!!!

(3)synchronized 加锁对象

        使用 synchronized 加锁时,我们需要确定一个对象,该对象也被称为锁对象。在 java 中任何一个对象,都可以作为锁对象!!!

        例如:成员变量、局部变量、静态变量、类对象....

        这些不同形态的对象,作为锁对象的时候没有任何区别,锁对象是指用来控制线程之间互斥的。

针对一个锁对象加锁,就会出现互斥。针对不同对象加锁,就不会互斥。    

         下面将根据不同的对象展示synchronized的用法

2、synchronized 几种用法

        我们先来看一个多线程修改一个对象的例子,如下两个线程对 counter 对象分别进行5w次修改,最后 count 预期结果应为 10w

class Counter{
    public int count = 0;
    public void increase(){
        count++;
    }
}

public class Demo2 {
    private static Counter counter = new Counter();

    public static void main(String[] args) throws InterruptedException {
        //搞两个线程,每个线程都针对这个 counter 来进行 5w 次自增
        // 预期结果 10w!!

        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
    }
}

结果如下:

 这就是两个线程同时修改一个变量所造成的问题,下面我将在不同地方加入 synchronized,向大家展示该效果 

 (1)修饰代码块

明确指定那个对象

synchronized(锁对象){
    //...
}

        1. this 对象

synchronized 里面写的锁对象是 this,也就相当于谁调用 increase,就针对谁进行加锁

 此时我们再来观察代码结果为:1w

 此时就说明,两个线程在执行过程中,产生了互斥也就是阻塞等待,一个线程执行完后,另一个线程才能执行!!

        2. object对象

创建一个 object 对象进行加锁

​  此时我们再来观察代码结果为:1w

 注意加 object 对象跟加 this对象效果一样,大部分情况在我们都是采用this对象

        4. 类对象 

添加类对象,相当于对整个类加锁

类对象: 类名.class

    public synchronized void increase(){
        synchronized (Counter.class){
            count++;
        }
    }

结果也为1w,也能起到线程阻塞等待的效果。 

(2)修饰普通方法

锁的 Counter 对象,这里跟修饰代码块中所this对象的效果是一样的。

(3)修饰静态方法

锁的 Counter 类的对象,为了方便展示,我把代码改成如下:

public class Counter {
    static int count;
    public synchronized static void increase(){
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                increase();
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

        给静态方法加 synchronized,锁的是整个类

结果如下:

 3、synchronized 特性

简单了解后,现在我们来具体看一下synchronized的几个特性

(1)互斥

        这个特性我们应该很熟悉了,synchronized 会起到互斥效果,某个线程执行到某个对象的 synchronized 中时,其他线程如果也执行到同一个对象 synchronized 就会阻塞等待。

  •  加锁:进入 synchronized 修饰的代码块
  •  解锁:退出 synchronized 修饰的代码块

注意:synchronized 用的锁是存在于Java对象里面的,也可以理解为,每个对象在内存中存储的时候,都存在一块内存表示当前的“锁定”状态。

  举个例子:“对象”比作公厕,锁门和开门代表厕所的两种状态(“有人/无人”)

    当一个线程占用厕所(“对象”),并上锁时,其他线程就只能排队等待(阻塞),直到这个线程释放该“对象”(释放)。


拓展:

  • 上一个线程解锁之后,下一个线程并不是立即就能获取到锁,而是靠操作系统来“唤醒”,这也就是操作系统调度的一部分工作。
  • 假设有 A B C 三个线程,线程 A 先获取到锁,然后 B 尝试获取锁,C 在 B之后尝试获取锁,此时 B 和 C 都在阻塞队列中排队等待。但当 A 释放锁之后,虽然 B 比 C 先来,但是 B 不一定就能获取到锁,而是和 C 重新竞争,并不遵守先来后到的规则。

(2)刷新内存

    synchronized 的工作过程

  1. 获得互斥锁
  2. 从主内存拷贝变量的最新副本到工作内存
  3. 执行代码
  4. 将更改后的共享变量的值刷新到主内存
  5. 释放互斥锁

    从上述可知,synchronized 拥有跟 volatile 关键字一样的效果,保证内存可见性,这里就不多加已说明了。(了解即可)

(3)可重入

synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题

什么是“不可重复”

       当一个线程加锁之后没有释放锁,然后又再次尝试加锁,按照对锁的设定,第二次加锁的时候就会阻塞等待。直到第一次的锁被释放,才能获取到第二个锁。

      但是释放第一个锁也是由该线程来完成的,但是这个线程正在阻塞等待,已经基本躺平,也就无法进行解锁操作。这时候就会死锁。​

 这样的锁就叫 不可重复锁

但是,在Java 中的使用 synchronized 上的锁是可重入的!!

例如:

        下面代码,对 increase1 和 increase3 两个方法都加了 synchronized,都是针对this对象(counter)加锁。

        线程执行 increase1 时对this加一次锁,当线程执行到 increase3 时对this又加一次锁。

        大家可以运行以下看看结果为几。

public class Counter {
    static int count;

    public synchronized void increase1(){
        increase2();
    }

    public void increase2(){
        increase3();
    }

    public synchronized void increase3(){
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t = new Thread(()->{
            counter.increase1();
        });

        t.start();
        t.join();
        System.out.println(count);
    }

}

 这串代码的结果为1,说明线程并没有被自己锁死,反而能进入再次进入自己加的锁内。这也就证明了 synchronized 的可重入性。

拓展:

在可重入锁的内部,包含了 “线程持有者” 和 “计数器” 两个信息

  • 如果某个线程加锁的时候,发现锁已经被人占了,但是恰好占用的正式自己,那么仍然可以继续获取到锁,并让计数器自增。
  • 解锁的时候计数器递减为0的时候,才真正释放锁(才能被别的线程获取到)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
synchronized关键字Java中用于实现线程同步的关键字。它可以用来修饰方法或代码块,以确保在多线程环境下的安全性。 synchronized关键字使用有以下几点需要注意: 1. synchronized是一种重量级的操作,会影响性能。因此,在使用synchronized时应尽可能减小同步块的范围,避免锁的竞争。 2. synchronized锁的范围应尽量小,只保护必要的代码块,避免对整个方法或对象进行锁定。这样可以提高程序的并发性能。 3. synchronized锁定的对象不应该被修改,否则可能会导致死锁的发生。因此,在使用synchronized时需要谨慎处理锁定的对象。 4. 在使用synchronized时,需要考虑线程间的协调和通信,以避免死锁和活锁的发生。这可以通过合理设计程序逻辑和使用其他同步机制来实现。 总的来说,synchronized关键字是一种常用的线程同步机制,可以确保在多线程环境下的数据安全性和一致性。但是在使用时需要注意性能问题和锁的范围,以及避免死锁和活锁的发生。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Synchronized 关键字详解](https://blog.csdn.net/swadian2008/article/details/99328700)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [举例讲解Javasynchronized关键字的用法](https://download.csdn.net/download/weixin_38724611/12798175)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱躺平的威威

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

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

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

打赏作者

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

抵扣说明:

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

余额充值