在Java中,对于synchronized关键字,大家看到的第一反应就是这个关键字是进行同步操作的,即得名“同步锁”。

  • 当用它来修饰方法和代码块时,默认当前的对象为锁的对象,即对象锁。

  • 当用来修饰类和静态方法时,默认当前的类为锁的对象

对象锁

修饰在方法上时,多个线程调用同一对象的同步方法时会阻塞,调用不同对象的同步方法时不会阻塞。

在多线程环境下,调用不同对象的同步方法:

public class SynchronizedDemo {

    public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.obj3();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.obj3();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0


在多线程环境下,调用同一对象的同步方法:

public class SynchronizedDemo {

    public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0


在多线程环境下,调用不同对象通过this修饰的局部代码块

public class SynchronizedDemo {

    public void synTest(){
synchronized (this){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0

对于this修饰的其实指的就是类的实例,所以它也属于对象锁,并不是类锁。


在多线程环境下,调用不同对象通过其他实例类修饰的局部代码块

public class SynchronizedDemo {

    public void synTest(){
String str = new String("lock");
synchronized (str){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(() -> {
demo1.synTest();
}); Thread t2 = new Thread(() -> {
demo2.synTest();
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0

我们可以看到,我们通过每次调用时实例一个String来进行同步代码块,但是并没有发生阻塞,因为每次生成的是一个实例String,锁的是String,每次都是不一样的,所以不会发生阻塞。


可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 调用不同对象的同步方法,不会发生阻塞
  • 调用相同对象的同步方法,会发生阻塞
  • 调用不同对象通过this修饰的局部代码块,不会发生阻塞
  • 调用不同对象通过其他实例类修饰的同步代码块,不会发生阻塞


类锁

在多线程环境下,多次调用类的静态同步方法:

public class SynchronizedDemo {

    public static synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
}); t1.start();
t2.start();
}

Output:

Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0

在多线程环境下,多次调用被类锁的代码块:

public class SynchronizedDemo {

    public void synTest(){
synchronized (SynchronizedDemo.class){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0
Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0

对于对象SynchronizedDemo.class,实际上就是SynchronizedDemo这个类,也就是对类进行加锁。

可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 多次调用静态的同步方法,会进行阻塞
  • 不同对象调用被类锁的同步代码块,会进行阻塞


类锁和对象锁同时存在

在多线程环境下,同时调用同一对象的类锁和对象锁

public class SynchronizedDemo {

    public static synchronized void synTestStatic() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public synchronized void synTest() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo(); Thread t1 = new Thread(() -> {
demo1.synTest();
}); Thread t2 = new Thread(() -> {
SynchronizedDemo.synTestStatic();
}); t1.start();
t2.start();
}
}

Output:

Thread-1 : 4
Thread-0 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0

我们可以到看到,在多线程环境下,类锁和对象锁同时存在的情况下,多线程访问时不会阻塞,因为他们不是同一个锁。


可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 类锁和对象锁同时存在的情况下,不会发生阻塞


总结

Java的类锁、对象锁和方法锁的更多相关文章

  1. 第31节:Java基础-类与对象

    前言 Java基础-类与对象,方法的重载,构造方法的重载,static关键字,main()方法,this关键字,包,访问权限,类的继承,继承性,方法的重写,super变量. 方法的重载:成员方法的重载 ...

  2. Java面向对象-类与对象

    Java面向对象-类与对象 类与对象的关系 我们通俗的举个例子,比如人类是一种类,张三这个人就是人类的具体的一个个体,也就是java中的对象:这就是一个类与对象的关系: 类的定义 下面看实例 类的创建 ...

  3. 关于Java构造类与对象的思考

    简单记录一下Java构造类与对象时的流程以及this和super对于特殊例子的分析. 首先,接着昨天的问题,我做出了几个变形: Pic1.原版: Pic2.去掉了T.foo方法中的this关键字: P ...

  4. java基础---类和对象(4)

    一. static关键字 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,整个类共享一份静态成员变量,该成员变量随着类的加载准备就绪,与是否创建对象无关 使用st ...

  5. day 23 对象的名称空间 类,对象属性和方法 封装 接口提供

    一.对象的特有名称空间 # 对象独有的名称空间:在产生对象时就赋初值 '''class ted: def func(): 当func里不存在参数时,调用时不需要给值 print('hah')ted.f ...

  6. 类的封装,property特性,类与对象的绑定方法和非绑定方法,

    类的封装 就是把数据或者方法封装起来 为什么要封装 封装数据的主要原因是:保护隐私 封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了,比如你 ...

  7. java类与对象(属性,方法)的使用

    ---恢复内容开始--- 类和对象是java编程中很重要的应该面向对象的一课,实际上可以将类看作对象的载体,它定义了对象所具有的功能.Java是面向对象的语言,因此掌握类与对象是学习Java语言的基础 ...

  8. Java面向对象——类,对象和方法

    1.类的概念 在生活中,说到类,可以联想到类别,同类,会想到一类人,一类事物等等.而这一类人或事物都是具有相同特征或特点和行为的,我们根据不同的特征或特点和行为将他们归类或分类.同时,当我们认识一个新 ...

  9. Java面向对象~类和对象&方法,类方法

    面向对象 概念:     1.同一类事物的抽象描述,不是具体的    2.类和对象的关系:        类 是抽象的.        对象 是具体的.    3.对象的体征,称为"属性&q ...

  10. JS创建类和对象(好多方法哟!)

    http://www.cnblogs.com/tiwlin/archive/2009/08/06/1540161.html 这是别人写的~~~我借来看看 JavaScript 创建类/对象的几种方式 ...

随机推荐

  1. 搭建环境-Eclipse配置Tomcat创建Servlet总结

    Descripton:Web开发:Eclipse的下载与安装,Tomcat下载和结合Eclipse的使用,Eclipse使用Servlet[记录下总结下] 一.Eclipse的下载与安装 下载地址 注 ...

  2. PHPStorm IDE 快捷键

    ⌘——Command ⌃ ——Control ⌥——Option/Alt ⇧——Shift ⇪——Caps Lock fn——功能键就是fn编辑 Command+alt+T 用 (if..else, ...

  3. 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)

    曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...

  4. 通过GSM模块发送经纬度求救信息。

    本博客作为实验笔记,仅供学习交流.(转载请注明出处) 本实验通过GSM模块:SIM900a,实现向特定手机发送sos求救信号,并且利用GPS模块:微科VK2828U7G5LF,将经纬度信息同时发送到手 ...

  5. java实现图片的上传和展示

    一.注意事项: 1,该项目主要采用的是springboot+thymeleaf框架 2,代码展示的为ajax完成图片上传(如果不用ajax只需要改变相应的form表单配置即可) 二.效果实现: 1,页 ...

  6. Redis linux 下安装

    Redis linux 下安装 下载Redis安装包,可以从Redis中文网站中下载 下载地址:http://www.redis.cn/download.html Redis4.0 稳定版本 使用&l ...

  7. Vue中el-form标签中的自定义el-select下拉框标签

    页面写死el-select下拉框标签: 通过v-for="item in stateArr"绑定,stateArr声明在Vue组件里面的data参数里面代码如下: <el-f ...

  8. python脚本如何同时运行多个

    当我们想一次运行多个py脚本的时候你想到了什么应用场景了吗?当你想同时并行的处理一些对象时你有什么好方法吗?下面我就简单的总结一些这方面的小技巧,方便大家根据情况灵活处理. 1 用一个py脚本运行多个 ...

  9. 关于C++线程池的实现的思考

    今天突然对前些日子一直很疑惑的c++线程池有了新的想法.其实所谓的线程池无非就是两个技术点,一个,多线程,指工作线程和主线程分离,或者说数据接收和处理分两个线程,一般就是讲需要运行的函数放到子线程执行 ...

  10. java多线程3:原子性,可见性,有序性

    概念 在了解线程安全问题之前,必须先知道为什么需要并发,并发给我们带来什么问题. 为什么需要并发,多线程? 时代的召唤,为了更充分的利用多核CPU的计算能力,多个线程程序可通过提高处理器的资源利用率来 ...