总结java中的synchronized锁

本文详细解释了Java中的`synchronized`关键字,包括其原子性、可见性、有序性特性,锁机制(悲观锁、乐观锁、轻量级/重量级),以及不同使用场景如同步方法、静态同步和同步块的示例。
摘要由CSDN通过智能技术生成

目录

synchronized的特性

synchronized的锁机制

synchronized的使用


synchronized的特性

  synchronized主要有三大特性:

  面试时经常拿synchronized关键字和volatile关键字的特性进行对比,synchronized关键字可以保证并发编程的三大特性:原子性、可见性、有序性,而volatile关键字只能保证可见性和有序性,不能保证原子性,也称为是轻量级的synchronized

  • 原子性:一个或多个操作要么全部执行成功,要么全部执行失败。synchronized关键字可以保证只有一个线程拿到锁,访问共享资源。
  • 可见性:当一个线程对共享变量进行修改后,其他线程可以立刻看到。执行synchronized时,会对应执行 lockunlock原子操作,保证可见性。
  • 有序性:程序的执行顺序会按照代码的先后顺序执行。

synchronized的锁机制

  synchronized既是悲观锁,也是乐观锁,既是轻量级锁,也是重量级锁。可重入锁,不是读写锁,是非公平锁。至于上述名词的意思,可以浏览这篇文章CSDN

synchronized的“自适应”是根据当前锁冲突的概率来调整锁策略。调整的过程分为以下阶段:

偏向锁:首次使用synchronized对对象进行加锁的时候,不是真的加锁。而只是做一个“标记”(非常轻量非常快,几乎没有开销)。如果没有别的线程尝试对这个对象加锁,就可以保持这个暧昧状态,一直到解锁。(解锁也就是修改一下上述标记,也几乎没有开销)上述过程,就相当于没有任何的加锁操作,速度是非常快的,也可以保证锁能够正常生效。但是,如果再偏向锁状态下,有某个线程也尝试来对这个对象加锁,立马把偏向锁升级成轻量级锁(真的有锁,真的有互斥了)

上述升级过程,针对一个锁对象来说,是不可逆的。一旦升级成了重量级锁,不会退回到轻量级锁。

synchronized的使用

synchronized主要有三种使用方式:修饰普通同步方法修饰静态同步方法修饰同步方法块

修饰普通同步方法

我们看下列代码:

public class ThreadDemo9 {
    private static int count;
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
       Thread t1 = new Thread(()->{
           for (int i = 0; i < 10000; i++) {
               synchronized (locker){
                   count++;
               }
           }
       });
       Thread t2 = new Thread(()->{
           for (int i = 0; i < 10000; i++) {
               synchronized (locker){
                   count++;
               }
           }
       });
       t1.start();
       t2.start();
       t1.join();
       t2.join();
        System.out.println("count = " + count);
    }
}

运行结果:

这就是使用synchronized保证线程安全,解决内存可见性问题的典型用法。关于内存可见性的详情,可以浏览这篇文章CSDN

修饰静态同步方法

示例代码如下:

class syncTest implements Runnable {
 ​
     private static int i = 0;   //共享资源
 ​
     private static synchronized void add() {
         i++;
     }
 ​
     @Override
     public void run() {
         for (int j = 0; j < 10000; j++) {
             add();
         }
     }
 ​
     public static void main(String[] args) throws Exception {
 ​
 //        syncTest syncTest = new syncTest();
 ​
         Thread t1 = new Thread(new syncTest());
         Thread t2 = new Thread(new syncTest());
 ​
         t1.start();
         t2.start();
 ​
         t1.join();
         t2.join();
 ​
         System.out.println(i);
     }
 }

运行结果为:

20000

即当synchronized作用于静态方法add(),锁就是当前的class对象

修饰同步方法块

如果某些情况下,整个方法体比较大,需要同步的代码只是一小部分,如果直接对整个方法体进行同步,会使得代码性能变差,这时只需要对一小部分代码进行同步即可。代码如下:

class syncTest implements Runnable {
 ​
     static int i = 0;   //共享资源
 ​
     @Override
     public void run() {
         //其他操作.......
         synchronized (this){   //this表示当前对象实例,这里还可以使用syncTest.class,表示class对象锁
             for (int j = 0; j < 10000; j++) {
                 i++;
             }
         }
 ​
     }
 ​
     public static void main(String[] args) throws Exception {
 ​
         syncTest syncTest = new syncTest();
 ​
         Thread t1 = new Thread(syncTest);
         Thread t2 = new Thread(syncTest);
 ​
         t1.start();
         t2.start();
 ​
         t1.join();
         t2.join();
 ​
         System.out.println(i);
     }
 }

输出结果:

20000

以上,关于synchronized希望对你有帮助。 

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值