synchronized有几种用法?
在 Java 语言中,保证线程安全性的主要手段是加锁,而 Java 中的锁主要有两种:synchronized 和 Lock,我们今天重点来看一下 synchronized 的几种用法。
用法简介
使用 synchronized 无需手动执行加锁和释放锁的操作,我们只需要声明 synchronized 关键字就可以了,JVM 层面会帮我们自动的进行加锁和释放锁的操作。
synchronized 可用于修饰普通方法、静态方法和代码块,接下来我们分别来看。
1、修饰普通方法
synchronized 修饰普通方法的用法如下:
/**
* synchronized 修饰普通方法
*/
public synchronized void method() {
// ....
}
当 synchronized 修饰普通方法时,被修饰的方法被称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。
2、修饰静态方法
synchronized 修饰静态方法和修饰普通方法类似,它的用法如下:
/**
* synchronized 修饰静态方法
*/
public static synchronized void staticMethod() {
// .......
}
当 synchronized 修饰静态方法时,其作用范围是整个程序,这个锁对于所有调用这个锁的对象都是互斥的。
所谓的互斥,指的是同一时间只能有一个线程能使用,其他线程只能排队等待。
修饰普通方法 VS 修饰静态方法
synchronized 修饰普通方法和静态方法看似相同,但二者完全不同,对于静态方法来说 synchronized 加锁是全局的,也就是整个程序运行期间,所有调用这个静态方法的对象都是互斥的,而普通方法是针对对象级别的,不同的对象对应着不同的锁,比如以下代码,同样是调用两次方法,但锁的获取完全不同,实现代码如下:
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SynchronizedUsage {
public static void main(String[] args) throws InterruptedException {
// 创建线程池同时执行任务
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 执行两次静态方法
threadPool.execute(() -> {
staticMethod();
});
threadPool.execute(() -> {
staticMethod();
});
// 执行两次普通方法
threadPool.execute(() -> {
SynchronizedUsage usage = new SynchronizedUsage();
usage.method();
});
threadPool.execute(() -> {
SynchronizedUsage usage2 = new SynchronizedUsage();
usage2.method();
});
}
/**
* synchronized 修饰普通方法
* 本方法的执行需要 3s(因为有 3s 的休眠时间)
*/
public synchronized void method() {
System.out.println("普通方法执行时间:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* synchronized 修饰静态方法
* 本方法的执行需要 3s(因为有 3s 的休眠时间)
*/
public static synchronized void staticMethod() {
System.out.println("静态方法执行时间:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上程序的执行结果如下:
从上述结果可以看出,静态方法加锁是全局的,针对的是所有调用者;而普通方法加锁是对象级别的,不同的对象拥有的锁也不同。
3、修饰代码块
我们在日常开发中,最常用的是给代码块加锁,而不是给方法加锁,因为给方法加锁,相当于给整个方法全部加锁,这样的话锁的粒度就太大了,程序的执行性能就会受到影响,所以通常情况下,我们会使用 synchronized 给代码块加锁,它的实现语法如下:
public void classMethod() throws InterruptedException {
// 前置代码...
// 加锁代码
synchronized (SynchronizedUsage.class) {
// ......
}
// 后置代码...
}
从上述代码我们可以看出,相比于修饰方法,修饰代码块需要自己手动指定加锁对象,加锁的对象通常使用 this 或 xxx.class 这样的形式来表示,比如以下代码:
// 加锁某个类
synchronized (SynchronizedUsage.class) {
// ......
}
// 加锁当前类对象
synchronized (this) {
// ......
}
this VS class
使用 synchronized 加锁 this 和 xxx.class 是完全不同的,当加锁 this 时,表示用当前的对象进行加锁,每个对象都对应了一把锁;而当使用 xxx.class 加锁时,表示使用某个类(而非类实例)来加锁,它是应用程序级别的,是全局生效的,如以下代码所示:
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SynchronizedUsageBlock {
public static void main(String[] args) throws InterruptedException {
// 创建线程池同时执行任务
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 执行两次 synchronized(this)
threadPool.execute(() -> {
SynchronizedUsageBlock usage = new SynchronizedUsageBlock();
usage.thisMethod();
});
threadPool.execute(() -> {
SynchronizedUsageBlock usage2 = new SynchronizedUsageBlock();
usage2.thisMethod();
});
// 执行两次 synchronized(xxx.class)
threadPool.execute(() -> {
SynchronizedUsageBlock usage3 = new SynchronizedUsageBlock();
usage3.classMethod();
});
threadPool.execute(() -> {
SynchronizedUsageBlock usage4 = new SynchronizedUsageBlock();
usage4.classMethod();
});
}
/**
* synchronized(this) 加锁
* 本方法的执行需要 3s(因为有 3s 的休眠时间)
*/
public void thisMethod() {
synchronized (this) {
System.out.println("synchronized(this) 加锁:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* synchronized(xxx.class) 加锁
* 本方法的执行需要 3s(因为有 3s 的休眠时间)
*/
public void classMethod() {
synchronized (SynchronizedUsageBlock.class) {
System.out.println("synchronized(xxx.class) 加锁:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
以上程序的执行结果如下:
总结
synchronized 用 3 种用法,用它可以来修饰普通方法、静态方法和代码块,其中最常用的是修饰代码块,而修饰代码块时需要指定一个加锁对象,这个加锁对象通常使用 this 或 xxx.class 来表示,当使用 this 时,表示使用当前对象来加锁,而使用 class 时,表示表示使用某个类(非类对象实例)来加锁,它是全局生效的。
是非审之于己,毁誉听之于人,得失安之于数。
公众号:Java面试真题解析
synchronized有几种用法?的更多相关文章
- Synchronized 有几种用法
我们都知道 Synchronized 是线程安全同步用的,大部分程序可能只会用到同步方法上面.其实 Synchronized 可以用到更多的场合,栈长列举了以下几个用法. 1.同步普通方法 这个也是我 ...
- Synchronized 有几种用法?
我们都知道 Synchronized 是线程安全同步用的,大部分程序可能只会用到同步方法上面.其实 Synchronized 可以用到更多的场合. 1.同步普通方法(锁实例对象) 这个也是我们用得最多 ...
- synchronized的四种用法
一 修饰方法 Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起 ...
- java synchronized的四种用法
一 修饰方法 Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来 ...
- synchronized的4种用法
1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.这时,线程获得的是成员锁,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程 ...
- Synchronized的几种用法
https://blog.csdn.net/luoweifu/article/details/46613015
- synchronized是什么,用法及原理
文章转Hollis博客 大家可以关注下,很多技术类型的文章 在再有人问你Java内存模型是什么,就把这篇文章发给他.中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提 ...
- 多线程Synchronized的两种锁
Synchronized的作用: 能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果 Synchronized的两个用法: 1)对象锁 包括方法锁(默认锁对象为this当前实例对 ...
- using 的三种用法
using 有哪三种用法? 1)引入命名空间. 2)给命名空间或者类型起别名. 3)划定作用域.自动释放资源,使用该方法的类型必须实现了 System.IDisposable接口,当对象脱离作用域之后 ...
随机推荐
- 使用阿里巴巴开源镜像站镜像——Kubernetes 镜像
镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 Kubernetes 镜像简介 Kubernetes 是一个开源系统,用于容器化应用的自动部署.扩缩和管理.它将构成应用的容器按逻辑单位进行分组以 ...
- zabbix图形中文乱码
别的贴子都是说到修改/usr/share/zabbix/include/defines.inc.php中的 define('ZBX_FONT_NAME', 'msyh'); define('ZBX_G ...
- 爬虫系列之Scrapy框架
一 scrapy框架简介 1 介绍 (1) 什么是Scrapy? Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,非常出名,非常强悍.所谓的框架就是一个已经被集成了各种功能(高性能 ...
- select 中的timeout
1. select 相关man 资料 /* According to POSIX.1-2001 */ #include <sys/select.h> /* According to ear ...
- 记录NLTK安装使用全过程--python
前言 之前做实验用到了情感分析,就下载了一下,这篇博客记录使用过程. 下载安装到实战详细步骤 NLTK下载安装 先使用pip install nltk 安装包 然后运行下面两行代码会弹出如图得GUI界 ...
- 两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
对. 因为equals()方法可以用开发者重写,hashCode()方法也可以由开发者来重写,因此它们是否相等并没有必然的关系. 如果对象要保存在HashSet或HashMap中,它们的equals( ...
- OpenSSL实现了5种信息摘要算法有哪些?
OpenSSL实现了5种信息摘要算法,分别是MD2.MD5.MDC2.SHA(SHA1)和RIPEMD.SHA算法事实上包括了SHA和SHA1两种信息摘要算法.此外,OpenSSL还实现了DSS标准中 ...
- springMVC和struts2的区别有哪些?
(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilte ...
- 访问修饰符 public,private,protected,以及不写(默认) 时的区别?
修饰符 当前类 同 包 子 类 其他包 public √ √ √ √ protecte d √ √ √ × default √ √ × × private √ × × × 类的成员不写访问修饰时默认为 ...
- 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?如何获取当前数据库版本?
一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几? 一般情况下,我们创建的表的类型是InnoDB,如果新增一条记录(不重启mysq ...