如何优雅地使用synchronized关键字

爬虫风控实战:当你的代理IP被数美滑块盯上时,如何优雅破解? 本文深入探讨了爬虫风控对抗中数美滑块验证码的破解与防御策略。通过分析数美滑块的多层防御机制,提出了代理IP池精细化管理、请求特征拟人化改造以及工程化滑块验证解决方案等系统性方法,帮助开发者有效降低验证码触发率,提升爬虫效率。 阅读详情

在Java中,synchronized 关键字是实现线程同步的基础工具,但其使用需要结合场景和性能考量。

一、选择合适的锁粒度

原则:在保证线程安全的前提下,尽量缩小同步范围,避免锁持有时间过长。

1. 方法级同步(粗粒度)
  • 适用场景:整个方法的逻辑都需要同步。
  • 示例:
    public class Counter {
        private int count = 0;
        
        // 同步整个方法(锁对象为this)
        public synchronized void increment() {
            count++;
        }
    }
    
2. 代码块级同步(细粒度)
  • 适用场景:仅部分代码需要同步。
  • 示例:
    public class Calculator {
        private final Object lock = new Object(); // 专用锁对象
        private int result = 0;
        
        public void compute() {
            // 非关键代码(无需同步)
            preProcess();
            
            // 仅关键部分加锁
            synchronized (lock) {
                result = performCriticalCalculation();
            }
            
            // 非关键代码(无需同步)
            postProcess();
        }
    }
    

二、优先使用专用锁对象

原则:避免使用 this 或类对象(Class)作为锁,减少锁竞争范围。

1. 错误示例(使用this)
public class BadLockExample {
    private final List<String> list = new ArrayList<>();
    
    public void add(String item) {
        synchronized (this) { // 锁范围过大,可能与其他方法冲突
            list.add(item);
        }
    }
    
    public void otherOperation() {
        synchronized (this) { // 与add()共享锁,可能导致不必要的阻塞
            // 其他操作
        }
    }
}
2. 正确示例(专用锁对象)
public class GoodLockExample {
    private final List<String> list = new ArrayList<>();
    private final Object listLock = new Object(); // 专门用于保护list的锁
    
    public void add(String item) {
        synchronized (listLock) { // 仅保护list相关操作
            list.add(item);
        }
    }
    
    public void otherOperation() {
        // 无需同步或使用其他锁
    }
}

三、静态同步方法与类锁

原则:静态同步方法使用类锁(Class 对象),与实例锁互不干扰。

1. 静态同步方法
public class StaticCounter {
    private static int count = 0;
    
    // 静态同步方法(锁对象为StaticCounter.class)
    public static synchronized void increment() {
        count++;
    }
}
2. 实例方法与静态方法的锁隔离
public class MixedLockExample {
    private int instanceCount = 0;
    private static int staticCount = 0;
    
    // 实例方法锁(this)
    public synchronized void instanceMethod() {
        instanceCount++;
    }
    
    // 静态方法锁(MixedLockExample.class)
    public static synchronized void staticMethod() {
        staticCount++;
    }
}

四、双重检查锁定(DCL)模式

场景:实现线程安全的懒汉式单例,需结合 volatile 关键字。

1. 错误示例(未使用volatile)
public class UnsafeSingleton {
    private static UnsafeSingleton instance;
    
    private UnsafeSingleton() {}
    
    public static UnsafeSingleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (UnsafeSingleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new UnsafeSingleton(); // 可能因重排序导致其他线程看到未初始化的对象
                }
            }
        }
        return instance;
    }
}
2. 正确示例(使用volatile)
public class SafeSingleton {
    private static volatile SafeSingleton instance; // 禁止指令重排序
    
    private SafeSingleton() {}
    
    public static SafeSingleton getInstance() {
        if (instance == null) { // 第一次检查(无锁)
            synchronized (SafeSingleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new SafeSingleton(); // 确保初始化完成后才对其他线程可见
                }
            }
        }
        return instance;
    }
}

五、生产者 - 消费者模型

场景:使用 wait()/notify() 实现线程间协作,需在同步块内调用。

1. 基于 synchronized 的实现
import java.util.LinkedList;
import java.util.Queue;

public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int MAX_SIZE = 10;
    
    // 生产者方法
    public void produce(int item) throws InterruptedException {
        synchronized (queue) { // 锁对象为共享队列
            while (queue.size() == MAX_SIZE) {
                queue.wait(); // 队列满时等待
            }
            queue.add(item);
            queue.notifyAll(); // 通知可能等待的消费者
        }
    }
    
    // 消费者方法
    public int consume() throws InterruptedException {
        synchronized (queue) {
            while (queue.isEmpty()) {
                queue.wait(); // 队列空时等待
            }
            int item = queue.poll();
            queue.notifyAll(); // 通知可能等待的生产者
            return item;
        }
    }
}
2. 优化:使用 notify() 替代 notifyAll()
public void produce(int item) throws InterruptedException {
    synchronized (queue) {
        while (queue.size() == MAX_SIZE) {
            queue.wait();
        }
        boolean wasEmpty = queue.isEmpty();
        queue.add(item);
        if (wasEmpty) { // 仅在队列从空变为非空时通知消费者
            queue.notify();
        }
    }
}

六、集合类的线程安全封装

场景:将非线程安全的集合转换为线程安全的实现。

1. 手动封装
import java.util.ArrayList;
import java.util.List;

public class SynchronizedList<E> {
    private final List<E> list = new ArrayList<>();
    private final Object lock = new Object();
    
    public void add(E element) {
        synchronized (lock) {
            list.add(element);
        }
    }
    
    public E get(int index) {
        synchronized (lock) {
            return list.get(index);
        }
    }
    
    public int size() {
        synchronized (lock) {
            return list.size();
        }
    }
}
2. 使用 Collections.synchronizedXXX
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class WrapperExample {
    private final List<String> list = Collections.synchronizedList(new ArrayList<>());
    
    public void add(String item) {
        list.add(item); // 内部已同步
    }
    
    public void iterate() {
        synchronized (list) { // 迭代时需手动同步
            for (String item : list) {
                // 处理元素
            }
        }
    }
}

七、性能优化技巧

  1. 减少锁持有时间:

    public void optimizedMethod() {
        // 非关键代码(无需同步)
        Object result;
        synchronized (this) {
            result = fetchData(); // 仅同步关键操作
        }
        process(result); // 非关键代码
    }
    
  2. 锁分段技术(适用于多资源独立操作):

    public class StripedLockExample {
        private final Object[] locks = new Object[16];
        private final Data[] data = new Data[16];
        
        public StripedLockExample() {
            for (int i = 0; i < 16; i++) {
                locks[i] = new Object();
                data[i] = new Data();
            }
        }
        
        public void update(int index) {
            synchronized (locks[index % 16]) { // 不同索引使用不同锁
                data[index % 16].update();
            }
        }
    }
    

八、常见误区与注意事项

  1. 避免在循环中同步:

    // 错误:每次循环都获取锁
    for (int i = 0; i < 1000; i++) {
        synchronized (lock) {
            count++;
        }
    }
    
    // 正确:仅在必要时同步
    synchronized (lock) {
        for (int i = 0; i < 1000; i++) {
            count++;
        }
    }
    
  2. 不要使用字符串常量或包装类作为锁:

    public void badLock() {
        synchronized ("LOCK") { // 字符串常量池可能导致意外的锁竞争
            // ...
        }
    }
    

九、总结

  1. 锁粒度:优先使用代码块同步,避免方法级同步。
  2. 锁对象:使用专用锁对象(private final Object),避免 this 或类对象。
  3. 原子性操作:对于简单原子操作,考虑使用 Atomic 类替代 synchronized。
  4. 线程协作:在同步块内使用 wait()/notify(),优先使用 notify() 减少唤醒线程数量。
  5. 性能考量:高并发场景下可考虑使用 ReentrantLock 或并发容器。

通过合理设计锁的范围和对象,结合线程协作机制,synchronized 关键字可以优雅且高效地解决多线程同步问题。

详解【计算机类&面试真题】军队文职考试——第7期:C盘格式化需要注意什么?| 两台笔记本连起来后ping不同,可能存在哪些问题?| 模拟信号到数字信号是如何转化的?| 计算机由哪些部件组成? 这是我分享的系列面试知识——军队文职(计算机类)面试真题详解(第7期)。本期分享:两台笔记本连起来后ping不同,你觉得可能存在哪些问题?模拟信号到数字信号是如何转化的?谈一谈计算机由哪些部件组成?如何加强计算机网络安全管理?若通过域名无法访问,而通过IP地址能访问。这是怎么回事?你将如何解决?谈一谈C盘格式化需要注意什么?开放类题目:如果应聘的岗位和你想的差距很远,你怎么办? 阅读详情

相关推荐

DDM-MIMO-FMCW雷达速度模糊与解模糊MATLAB仿真

本文深入解析了DDM-MIMO-FMCW雷达中的速度模糊现象及其解模糊方法,通过MATLAB仿真详细展示了相位编码解调、速度扩展算法和混合波形设计三大解决方案。文章还提供了参数配置、信号建模和结果可视化的实用技巧,并分享了工程实践中的避坑指南,帮助开发者高效解决雷达信号处理中的速度模糊问题。

weixin_31528001的博客 350

JUC并发—3.volatile和synchronized原理

之后在执行线程0的指令时,便会在该CPU的高速缓存里读取flag的值。于是此时线程1的操作栈顶的n值,就变成了过期数据,所以线程1执行put_field指令后就会把较小的n值同步回主内存中。加锁的方式是首先在每个线程的栈帧中分配一个Lock Record,然后把锁对象中的Mark Word拷贝到Lock Record中,最后把锁对象的Mark Word的指针指向Lock Record。static修饰的成员变量和方法,它们的生命周期都是属于类级别的,它们随着类的定义被分配和装载到内存,随着类被卸载而回收。

weixin_55010563的博客 803

Codex Skill:AI编程助手的专项能力扩展与实战指南

在AI编程助手领域,Skill(技能)是一种标准化的能力扩展机制,它通过封装特定任务所需的指令、资源和脚本,解决AI模型在专项任务上的能力局限。其核心原理是将专业知识和工作流程模块化,让通用AI助手能够像领域专家一样执行复杂任务。从技术价值看,Skill不仅提升了代码生成的准确性,更重要的是实现了业务流程的自动化集成,如代码规范检查、API文档生成等具体场景。这种机制特别适用于需要结合内部工具链和特定业务逻辑的企业环境,能够显著提升开发效率和质量。通过安装PPT生成、SpringBoot项目创建等专项Ski

weixin_33672109的博客 414

java多线程之synchronized 和 volatile - (effective java item66中的一处错误)

我们都知道线程有个stop怎么停止?先给出一个 错误的方式1 public class ThreadStop { //失败的方法1 这种在一秒后stopThread虽然被修改了,但是backThread并没有获取到该信息 public static boolean stopThread; public static void ma...

进化的深山猿 8437

手把手教你使用ESM-2进行蛋白质结构预测:从安装到实战

本文详细介绍了如何使用ESM-2蛋白质语言模型进行蛋白质结构预测,从环境准备、模型安装到实战应用。通过逐步指导,读者可以掌握ESM-2的配置、单序列预测、结果可视化及高级优化技巧,适用于生物信息学研究和药物开发。

weixin_29306875的博客 534

三极管特性曲线看不懂?手把手教你用Multisim仿真分析放大电路频率响应

本文针对模拟电路初学者,详细讲解如何利用Multisim软件进行电路仿真,将抽象的三极管特性曲线与放大电路频率响应分析变得直观可视。通过手把手教学,指导读者从导入三极管SPICE模型、仿真输出特性曲线,到分析动态工作点,最终使用波特图仪深入分析频率响应并进行补偿设计,有效帮助理解电路原理并指导实践调试。

weixin_29230059的博客 123

Java语言在多线程环境下如何优雅地处理共享资源竞争问题

例如,Semaphore可以控制同时访问特定资源的线程数量,而CountDownLatch则允许一个或多个线程等待其他线程完成操作,这些工具为处理多线程资源竞争提供了更高级的抽象。这些类利用CAS(Compare-And-Swap)等底层硬件指令来保证操作的原子性,避免了锁的使用,从而减少了线程上下文切换的开销,提升了程序在高并发场景下的性能。例如,使用同步方法可以保证对实例变量的安全访问,而同步代码块则提供了更细粒度的控制,允许指定特定的锁对象,从而减少不必要的性能开销。

u010891102的博客 320

农业杂草检测——棉花杂草数据集

然后将本层13×13的特征图谱经过×2上采样扩充到26×26,并与上层中26×26的特征图谱拼接,得到新的26×26的特征图谱,在该特征尺度上进行第二次预测,此时得到的特征图谱是相对输入图像16倍下采样,具有中尺度感受野,适合检测中等尺度对象;通过目标检测,能够及时清除杂草,减少病虫害的滋生和传播,保障棉花的健康生长,并有助于降低生产成本。对于地膜覆盖的棉田,由于膜内耕作层土温高、底墒好,杂草出苗早而集中,一般覆膜后5 - 7天杂草陆续出土,在墒情好的情况下,15天左右即形成出土高峰。

qq_38220914的博客 1247

synchronized实现两个线程交替运行

背景 用两个线程交替输出A-Z和1-26,即一个线程输出A-Z,另一个线程输出1-26 而且是交替形式 线程1输出A——线程二输出1 线程1输出B——线程二输出2 线程1输出C——线程二输出3 以此类推 分析 主要考察线程之间的通信,思路就是创建两个线程 在一个线程输出一个内容之后,自己进入阻塞,去唤醒另一个线程 另一个线程同样,输出一个内容之后,自己进入阻塞,去唤醒另一个线程 代码实现(一) public class AlternateCover { public static void m

浪丶荡 787

Win10系统怎么打开Microsoft帐户?Win10系统打开Microsoft的帐户方法

也被加入了越来越多的功能,我们在使用电脑的时候,很对用户会打开Micorsofy账户功能,搜索个人邮件信息,照片等等,还能将个人的数据备份到账户内,那要怎么打开账户呢,下面带给大家方法。以上就是小编给大家整理的win10系统打开Microsoft帐户的具体操作方法,方法简单易懂,有需要的朋友可以看一看,希望这篇教程对大家有所帮助。5、最后我们将“Microsoft帐户”选项下方的开关按钮向右滑动将其打开,如下图所示,这样就成功打开wMicrosoft帐户功能了。我们在窗口左侧点击“设置”选项即可。

2401_88800025的博客 278

【嘉立创EDA】PCB金属包边设计快捷方法

本文使用嘉立创EDA,描述如何在PCB设计时,快速增加边框包边设计,特别是金属包边(伪)——这里标记(伪)是因为该这里的包边特指不含边缘壁。请参考本文时特别注意!本文将此过程记录,以供有需要的读者参考。

l64481000的博客 1744

Java--多线程细节深化

目录索引优雅的停止线程后台守护线程volatile关键字 优雅的停止线程 线程的启动使用的是Thread类的start()方法,而它还提供了 stop() 方法去停止线程、destroy() 方法去销毁线程、suspend() 方法去挂起线程暂停执行 以及 resume() 方法使挂起线程的恢复但现在都被废除了(因为这些方法可能导致死锁) 所以要想去停止线程可以通过flag去控制 public class ThreadDemo { public static boolean flag = true ;

qq_43337254的博客 214

Oracle EBS FA 创建成批增加执行时间长且报错

摘要 Oracle EBS系统在执行"创建成批增"请求时出现长时间运行并报错(ORA-00603和ORA-03114)。

陈显平的专栏-Oracle EBS 174

Java核心技术在多线程环境下如何优雅地处理资源竞争

优雅地处理多线程资源竞争是Java高级开发的必备技能。从最基础的关键字,到灵活的,再到高效的原子变量和无锁算法,最后到开箱即用的并发集合,Java提供了多种不同粒度和适用场景的解决方案。开发者应根据具体的业务需求、性能要求和复杂度,选择最合适的工具,从而编写出既正确又高效的多线程程序。

sinian18601的博客 317

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

多线程的启动方式以及synchronized原理

介绍多线程的启动方式、sleep、yield、interrupt、join以及synchronized原理

qq_40223516的博客 401

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

线程使用

1. 定义 1.1定义线程有两种方式 继承Thread类 实现Runnable接口(因Java单继承,推荐使用实现接口的方式) 1.2语法格式 继承Thread类 // 线程入口方法 @Override public void run() { } } 实现Runnable接口 // 线程入口方法 @Override public void run() { } } 2. 创建和启动 线程启动是通过调用线程类的start()方法,使线程进入就绪状态。CPU时间片到来,则线程真正执

a290575479的博客 280

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Java多线程开发--多线程深入话题

内容学习于:edu.aliyun.com 1. 优雅的停止线程   在多线程操作之中如果要启动多线程肯定使用的是Thread类中的start()方法,而如果对于多线程需要进行停止处理,Thread类原本提供有stop()方法,但是对于这些方法从JDK 1.2版本开始就已经将其废除了,而且一直到现在也不再建议出现在你的代码之中,而除了stop()之外还有几个方法也被禁用了:   已经过期的方法...

xzzz2020的博客 243

多线程基础

1. 单线程中的每个操作 happend-before任意后续操作2. 对volatile变量的写入,happen-before对应后续对这个变量的读取。3. 对synchronized的解锁,happen-before对应后续对这个锁的加锁。4. happen-before的传递性5. final变量的写可见于后续的final变量的读。(3)原理在编译器和CPU层面都有对应的指令,也就是内存屏障。编译器的内存屏障只是为了告诉编译器不要对执行进行重排序。

cggghh的博客 916

并发编程-多线程&并发设计原理

Java是一个支持多线程的开发语言。多线程可以在包含多个CPU核心的机器上同时处理多个不同的任务,优化资源的使用率,提升程序的效率。在一些性能要求比较高的场合,多线程是java程序调优的重要方面。并发编程三要素原子性:即一个不可再分割的粒子。在Java中原子性指的是一个或者多个操作要么全部执行成功要么全部执行失败有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)可见性:当多个线程访问同一个变量时,如果其中一个线程对其进行了修改,其他线程能立即获取到最新的值。

weixin_44796239的博客 1184

[并发实现原理] 01 多线程基础

一、线程的优雅关闭 1、为什么需要优雅关闭?运行一半的线程能否强制杀死? 杀死线程的方法:stop()、destory()函数 不建议使用,强制杀死线程,则线程中所使用的资源不能正常关闭(例如文件描述符、网络连接等)。 一个线程一旦运行起来,就不要去强行打断它,合理的关闭办法是让其运行完(也就是函数执行完毕),干净地释放掉所有资源,然后退出。 如果是一个不断循环运行的线程,就需要用到线程间的通信机制,让主线程通知其退出。 2、守护线程 介绍 a: 当在一个JVM进程

promiseful的博客 179

多线程深入

在多线程的操作之中启动肯定是thread类中的start方法,而如果对于多线程停职处理,thread原本提供stop方法,但是对于这些方法从jdk1.2已经移除,不建议出现到代码之中。而如果一个属性追加volatile关键字,表示的是不使用副本,直接操作变量,相当于节约了不拷贝副本,重新保存的步骤。可以发现所有的守护线程都是围绕在用户线程的周围,如果程序执行完毕,守护线程就小时,最大的守护线程就是GC线程。主要是在属性定义上使用的,表示此属性是直接数据操作,而不进行副本的拷贝处理。通过while(布尔){

hxw0427的博客 526

Java多线程03:多线程深入+多线程综合实例

Java高级编程03 本文基于 阿里云大学:Java高级编程 整理记录,仅用于个人学习/交流使用。 目录标题Java高级编程03五、多线程深入话题优雅的停止线程后台守护线程volatile关键字六、多线程综合案例数字加减生产电脑竞争抢答 五、多线程深入话题 优雅的停止线程 在多线程操作之中如果要启动多线程肯定使用的是Thread类中的 start()方法,而如果对于多线程需要进行停止处理,Thread类原本提供有 stop()方法,但是对于这些方法从JDK1.2版本开始就已经将其废除了。而且一直到现在也

耿鬼不会笑的博客 292

java不安全的线程和如何正确关闭线程

我先简单举一个线程不安全的例子 大家请自觉把i–当作银行取款过程,基本所有线程例子都是以银行取款为例,因为银行取款是最需要安全的线程的了 public class demo1 extends Thread{ private int i=10; @Override public void run() { i--; //想等于一次取款过程 System.out....

weixin_43486804的博客 1万+

第一章 多线程基础

1.1线程的优雅关闭 1.1.1 stop()和destory函数不能关闭线程 1.1.2 线程分为守护线程和非守护线程 守护线程:①.在main()函数执行完成后守护线程自动退出;②.守护线程不影响jvm虚拟机的退出 需要在t.start()前面添加t.setDeamon(true)方式设置为守护线程 典型案例:gc回收线程就是守护线程。 非守护线程:在main()函数执行完成后不退出,只有自己线程执行完成才退出;②.非守护线程必须全部退出了jvm虚拟机才退出。 1.1.3 设置状态标志位

龚礼鹏的博客 608

java 多线程深入_java高级-多线程深入

一、优雅的停止线程开启线程的方式是:调用Thread中的start()方法,停止线程的方式:原本Thread提供了stop()方法,但是在JDK1.2坂本后就已经被禁用了。除了stop(),还有几个方法被禁用例如:1.停止多线程:public void stop();2.销毁多线程:public void destory();3.挂起线程:public final void suspend();4...

weixin_33630090的博客 126

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 292
上一篇: 如何在使用ReentrantLock类时避免死锁?
下一篇: MySQL主要数据类型详解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值