架构系列——你可能不知道的那些synchronized使用细节

简介: 架构系列——你可能不知道的那些synchronized使用细节

前言

synchronized可以为任意对象加锁,用法比较灵活,语法如下

(1)修饰代码块,作用于调用的对象;

(2)修饰方法,作用于调用的对象;

(3)修饰静态方法,作用于所有对象;

(4)修饰类,作用于所有对象。

synchronized取得的锁都是对象锁,而不是把一段代码(或者方法)当成锁!

使用synchronized时,应该注意以下细节问题:

1、synchronized锁的重入性

如下列代码所示,类似于ReentrantLock

在method1中没有释放锁的情况下,可以继续调用synchronized修饰的method2

public class SyncDubbo {
  public synchronized void method1(){
    System.out.println("method1...");
    method2();
  }
  public synchronized void method2(){
    System.out.println("method2...");
    method3();
  }
  public synchronized void method3(){
    System.out.println("method3...");
  }
  public static void main(String[] args) {
    final SyncDubbo sd = new SyncDubbo();
    Thread t1 = new Thread(new Runnable(){
      @Override
      public void run() {
        // TODO Auto-generated method stub
        sd.method1();
      }
    }, "t1");
    t1.start();
  }
}

如下列代码所示,在子类与父类之间相互调用也运用了synchronized的重用性

public class FatherSon {
  static class Father {
    public int num = 10;
    public synchronized void method1(){
      try {
        num --;
        System.out.println("Father num = " + num);
        Thread.sleep(100);
      } catch (Exception e) {
        // TODO: handle exception
      }
    }
  }
  static class Son extends Father {
    public synchronized void method2(){
      try {
        while (num >0) {
          num --;
          System.out.println("Son num = " + num);
          Thread.sleep(100);
          this.method1();
        }
      } catch (Exception e) {
        // TODO: handle exception
      }
    }
  }
  public static void main(String[] args) {
    Thread t1 = new Thread(new Runnable(){
      @Override
      public void run() {
        // TODO Auto-generated method stub
        Son son = new Son();
        son.method2();
      }
    }, "t1");
    t1.start();
  }
}

2、不要使用字符串常量作为锁

如下列代码所示,使用了字符串常量作为锁,那么t1和t2运行之后将会一直在t1中出现死循环,t2永远拿不到锁!

解决:可以使用 new String("字符串常量") 作为锁

public class StringLock {
  public void method(){
    synchronized("字符串常量"){
      try {
        while (true){
          System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
          Thread.sleep(1000);
          System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
        }
      } catch (Exception e) {
        // TODO: handle exception
      }
    }
  }
  public static void main(String[] args) {
    final StringLock sl = new StringLock();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        // TODO Auto-generated method stub
        sl.method();
      }
    }, "t1");
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        // TODO Auto-generated method stub
        sl.method();
      }
    }, "t2");
    t1.start();
    t2.start();
  }
}

3、锁对象的改变问题

如下列代码所示,使用synchronized锁住了一个对象,并且在代码里重新new了一个对象,导致锁对象改变。

这样在t1还没有运行完代码的时候,t2就已经可以拿到锁了,显然会出现问题!

解决:不要改变锁对象(但是改变对象的属性对代码不会有影响,比如锁是一个人,那么可以改变这个人的身高、年龄等)

public class ChangeLock {
  Object obj = new Object();
  public void method(){
    synchronized(obj){
      try {
        System.out.println("当前线程:" + Thread.currentThread().getName() + "开始");
        //从这里改变锁对象
        obj = new Object();
        Thread.sleep(3000);
        System.out.println("当前线程:" + Thread.currentThread().getName() + "结束");
      } catch (Exception e) {
        // TODO: handle exception
      }
    }
  }
  public static void main(String[] args) {
    final ChangeLock cl = new ChangeLock();
    Thread t1 = new Thread(new Runnable() {
      @Override
      public void run() {
        // TODO Auto-generated method stub
        cl.method();
      }
    }, "t1");
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        // TODO Auto-generated method stub
        cl.method();
      }
    }, "t2");
    t1.start();
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    t2.start();
  }
}
相关文章
|
6月前
|
架构师 Java API
阿里P7架构师带你深入剖析synchronized的实现原理
显示锁ReentrantLock的内部同步依赖于AQS(AbstractQueuedSynchronizer),因此,分析ReentrantLock必然涉及AQS。
47 1
|
21天前
|
缓存 监控 API
探索微服务架构中的API网关模式
【10月更文挑战第5天】随着微服务架构的兴起,企业纷纷采用这一模式构建复杂应用。在这种架构下,应用被拆分成若干小型、独立的服务,每个服务围绕特定业务功能构建并通过HTTP协议协作。随着服务数量增加,统一管理这些服务间的交互变得至关重要。API网关作为微服务架构的关键组件,承担起路由请求、聚合数据、处理认证与授权等功能。本文通过一个在线零售平台的具体案例,探讨API网关的优势及其实现细节,展示其在简化客户端集成、提升安全性和性能方面的关键作用。
63 2
|
25天前
|
存储 缓存 监控
探索微服务架构中的API网关模式
【10月更文挑战第1天】探索微服务架构中的API网关模式
76 2
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
5天前
|
监控 Cloud Native Java
云原生架构下微服务治理策略与实践####
【10月更文挑战第20天】 本文深入探讨了云原生环境下微服务架构的治理策略,通过分析当前技术趋势与挑战,提出了一系列高效、可扩展的微服务治理最佳实践方案。不同于传统摘要概述内容要点,本部分直接聚焦于治理核心——如何在动态多变的分布式系统中实现服务的自动发现、配置管理、流量控制及故障恢复,旨在为开发者提供一套系统性的方法论,助力企业在云端构建更加健壮、灵活的应用程序。 ####
46 10
|
5天前
|
运维 Cloud Native 持续交付
云原生架构下的微服务设计原则与实践####
【10月更文挑战第20天】 本文深入探讨了云原生环境中微服务设计的几大核心原则,包括服务的细粒度划分、无状态性、独立部署、自动化管理及容错机制。通过分析这些原则背后的技术逻辑与业务价值,结合具体案例,展示了如何在现代云平台上实现高效、灵活且可扩展的微服务架构,以应对快速变化的市场需求和技术挑战。 ####
25 7
|
5天前
|
监控 Cloud Native 持续交付
云原生架构下微服务的最佳实践与挑战####
【10月更文挑战第20天】 本文深入探讨了云原生架构在现代软件开发中的应用,特别是针对微服务设计模式的最优实践与面临的主要挑战。通过分析容器化、持续集成/持续部署(CI/CD)、服务网格等关键技术,阐述了如何高效构建、部署及运维微服务系统。同时,文章也指出了在云原生转型过程中常见的难题,如服务间的复杂通信、安全性问题以及监控与可观测性的实现,为开发者和企业提供了宝贵的策略指导和解决方案建议。 ####
29 5
|
4天前
|
Kubernetes Cloud Native 持续交付
云原生架构下的微服务设计原则与最佳实践##
在数字化转型的浪潮中,云原生技术以其高效、灵活和可扩展的特性成为企业IT架构转型的首选。本文深入探讨了云原生架构的核心理念,聚焦于微服务设计的关键原则与实施策略,旨在为开发者提供一套系统性的方法论,以应对复杂多变的业务需求和技术挑战。通过分析真实案例,揭示了如何有效利用容器化、持续集成/持续部署(CI/CD)、服务网格等关键技术,构建高性能、易维护的云原生应用。文章还强调了文化与组织变革在云原生转型过程中的重要性,为企业顺利过渡到云原生时代提供了宝贵的见解。 ##
|
11天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型加速的今天,云原生技术以其高效、灵活、可扩展的特性成为企业IT架构转型的首选。本文深入探讨了云原生环境下微服务治理的策略与实践路径,旨在为读者提供一个系统性的微服务治理框架,涵盖从服务设计、部署、监控到运维的全生命周期管理,助力企业在云端构建更加稳定、高效的业务系统。 ####