在Java中,synchronized关键字提供了内置的支持来实现同步访问共享资源,以避免并发问题。synchronized主要有三种加锁方式:

1.同步实例方法

当一个实例方法被声明为synchronized时,该方法将同一时间只能被一个线程访问。锁是当前对象实例(即this)。

public class SynchronizedInstanceMethod {
public synchronized void doSomething() {
// 同步代码块
// 同一时间只能有一个线程执行这里的代码
} public static void main(String[] args) {
SynchronizedInstanceMethod obj = new SynchronizedInstanceMethod();
// 创建多个线程访问obj的doSomething方法,它们将串行执行
// ...
}
}

当我们创建了两个线程来并发地增加计数器,由于我们使用了synchronized,因此计数器的增加线程是安全的,即使两个线程都在尝试修改同一个共享变量。在同步实例方法中,锁分别是实例对象和类对象。

public class SynchronizedInstanceMethodExample {
private int count = 0; // 同步实例方法,锁定的是当前对象的实例(this)
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " incremented count to " + count);
} public static void main(String[] args) {
final SynchronizedInstanceMethodExample example = new SynchronizedInstanceMethodExample(); // 创建两个线程来增加计数器
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
}, "Thread-1"); Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
}, "Thread-2"); // 启动线程
thread1.start();
thread2.start(); // 等待线程完成
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
} // 输出最终计数
System.out.println("Final count: " + example.count);
}
}

2.同步静态方法

当一个静态方法被声明为synchronized时,该方法将同一时间只能被一个线程访问。锁是Class对象,而不是实例对象。

public class SynchronizedStaticMethod {
public static synchronized void doSomethingStatic() {
// 同步代码块
// 同一时间只能有一个线程执行这里的代码
} public static void main(String[] args) {
// 创建多个线程访问SynchronizedStaticMethod的doSomethingStatic方法,它们将串行执行
// ...
}
}

当我们创建了两个线程来并发地增加计数器,由于我们使用了synchronized,因此计数器的增加线程是安全的,即使两个线程都在尝试修改同一个共享变量。在同步静态方法中,锁分别也是实例对象和类对象。

public class SynchronizedStaticMethodExample {
private static int count = 0; // 同步静态方法,锁定的是当前对象的类(Class)对象
public static synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " incremented static count to " + count);
} public static void main(String[] args) {
// 创建两个线程来增加计数器
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
}, "Thread-1"); Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
}, "Thread-2"); // 启动线程...
// 等待线程完成...
// 输出最终计数...
// (与上面的例子类似,但省略了重复的代码)
}
}

3.同步代码块

我们可以使用synchronized关键字来定义一个代码块,而不是整个方法。在这种情况下,你可以指定要获取的锁对象。这提供了更细粒度的同步控制。

public class SynchronizedBlock {
private final Object lock = new Object(); // 用于同步的锁对象 public void doSomething() {
synchronized (lock) {
// 同步代码块
// 同一时间只有一个线程能够执行这里的代码
} // 这里的代码不受同步代码块的约束
} public static void main(String[] args) {
SynchronizedBlock obj = new SynchronizedBlock();
// 创建多个线程访问obj的doSomething方法,但只有在synchronized块中的代码将串行执行
// ...
}
}

在上面的SynchronizedBlock类中,我们创建了一个私有的Object实例lock作为锁对象。当线程进入synchronized (lock)块时,它会尝试获取lock对象的锁。如果锁已经被其他线程持有,那么该线程将被阻塞,直到锁被释放。

当我们创建了两个线程来并发地增加计数器,同步代码块的例子中,我们显式地指定了一个对象作为锁。

public class SynchronizedBlockExample {
private final Object lock = new Object(); // 用于同步的锁对象
private int count = 0; // 同步代码块,指定了锁对象
public void increment() {
synchronized (lock) {
count++;
System.out.println(Thread.currentThread().getName() + " incremented count to " + count);
}
} public static void main(String[] args) {
// 类似于上面的例子,但使用SynchronizedBlockExample的increment方法
// ...(省略了重复的代码)
}
}

注意:使用synchronized时应该尽量避免在持有锁的情况下执行耗时的操作,因为这会导致其他等待锁的线程长时间阻塞。同时,过度使用synchronized可能会导致性能下降,因为它会引入线程间的竞争和可能的上下文切换。在设计并发程序时,应该仔细考虑同步的粒度,并可能使用其他并发工具(如ReentrantLockSemaphoreCountDownLatch等)来提供更细粒度的控制。

java的synchronized有几种加锁方式的更多相关文章

  1. 015-线程同步-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

    一.synchronized概述基本使用 为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题. syn ...

  2. 【转载】Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍

    转载地址:http://blog.csdn.net/truong/article/details/46711045 关键字:Redis的Java客户端Jedis的八种调用方式(事务.管道.分布式…)介 ...

  3. Java多线程有哪几种实现方式? Java中的类如何保证线程安全? 请说明ThreadLocal的用法和适用场景

    java的同步机制,大概是通过:1.synchronized:2.Object方法中的wait,notify:3.ThreadLocal机制来实现的, 其中synchronized有两种用法:1.对类 ...

  4. JAVA中单例模式的几种实现方式

    1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...

  5. 使用java配置定时任务的几种配置方式及示例

    Spring定时器,主要有两种实现方式,包括Java Timer定时和Quartz定时器! 1.Java Timer定时 首先继承java.util.TimerTask类实现run方法 package ...

  6. Java中String对象两种赋值方式的区别

    本文修改于:https://www.zhihu.com/question/29884421/answer/113785601 前言:在java中,String有两种赋值方式,第一种是通过“字面量”赋值 ...

  7. 细说java中Map的两种迭代方式

    曾经对java中迭代方式总是迷迷糊糊的,今天总算弄懂了.特意的总结了一下.基本是算是理解透彻了. 1.再说Map之前先说下Iterator: Iterator主要用于遍历(即迭代訪问)Collecti ...

  8. Java中Map的4种遍历方式

    第一种方式:这是平常用的最多也最可取的一种遍历方式. for (Map.Entry<String, Object> entry : map.entrySet()) { System.out ...

  9. 基于Java的二叉树的三种遍历方式的递归与非递归实现

    二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...

  10. Java线程池的四种创建方式

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFix ...

随机推荐

  1. OpenHarmony 官网文档有哪些上新?下篇:设备开发文档上新

    为了方便社区开发者更易获取 OpenAtom OpenHarmony(以下简称"OpenHarmony")相关文档,暨上篇应用开发文档上新内容,SIG Docs  小组同步准备了设 ...

  2. OpenAtom OpenHarmony分论坛圆满举办,生态与产业发展迈向新征程

    7月27日,2022开放原子全球开源峰会OpenAtom OpenHarmony分论坛在北京成功举办.本次论坛以"万物互联,使能千行百业"为主题,OpenHarmony共建单位.生 ...

  3. C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析

    C# Methods 方法是一段代码,只有在调用时才会运行. 您可以将数据(称为参数)传递给方法. 方法用于执行某些操作,也被称为函数. 为什么使用方法?为了重用代码:定义一次代码,然后多次使用. 创 ...

  4. C#_面试题2

    1 :维护数据库的完整性.一致性.你喜欢用触发器还是自写业务逻辑?为什么答:尽可能用约束(包括CHECK.主键.唯一键.外键.非空字段)实现,这种方式的效率最好:其次用触发器,这种方式可以保证无论何种 ...

  5. Python设计模式----3.单例模式

    单例模式:主要目的是确保某一个类只有一个实例存在 代码: class A(): def __new__(self, *args, **kwargs): if not hasattr(self, 'na ...

  6. MogDB/openGauss 自定义snmptrapd告警信息

    MogDB/openGauss 自定义 snmptrapd 告警信息 在实际使用中,默认的报警规则信息并不能很好的满足 snmp 服务端的需求,需要定制化报警信息,这里以添加 ip 为例,看似一个简单 ...

  7. Redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?

    面试官心理分析 这个也是线上非常常见的一个问题,就是多客户端同时并发写一个 key,可能本来应该先到的数据后到了,导致数据版本错了:或者是多客户端同时获取一个 key,修改值之后再写回去,只要顺序错了 ...

  8. K8s集群nginx-ingress监控告警最佳实践

    本文分享自华为云社区<K8s集群nginx-ingress监控告警最佳实践>,作者:可以交个朋友. 一 背景 nginx-ingress作为K8s集群中的关键组成部分.主要负责k8s集群中 ...

  9. byte[]类型与datetime日期转换

    在C#中,Timestamp通常表示为一个长整型(long)变量.这是因为它表示自1970年1月1日00:00:00 UTC以来的毫秒数.然而,在某些情况下,例如在处理数据库中的Timestamp时, ...

  10. 《最新出炉》系列入门篇-Python+Playwright自动化测试-40-录制生成脚本

    1.简介 各种自动化框架都会有脚本录制功能, playwright这么牛叉当然也不例外.很早之前的selenium.Jmeter工具,发展到每种浏览器都有对应的录制插件.今天我们就来看下微软自动化框架 ...