在JAVA多线程编程中,将需要并发执行的代码放在Thread类的run方法里面,然后创建多个Thread类的对象,调用start()方法,线程启动执行。

当某段代码需要互斥时,可以用 synchronized 关键字修饰,这里讨论 synchronized 关键字修饰方法时,是如何互斥的。

synchronized 修饰方法时锁定的是调用该方法的对象。它并不能使调用该方法的多个对象在执行顺序上互斥。

下面举个具体的例子说明:

Test.java 通过 implements Runnable 成为一个线程类,它有一个MethodSync实例变量,这样,每当实例化一个Test对象时(相当于创建一个线程)就会初始化一个相应的MethodSync对象。然后在Test类的run()方法里面调用 synchronized 修饰的方法。

Test.java

 public class Test implements Runnable{
private String name;
// private static MethodSync methodSync = new MethodSync();
private MethodSync methodSync = new MethodSync(); public Test(String name){
this.name = name;
} @Override
public void run() {
methodSync.method(name);
} public static void main(String[] args) {
Thread t1 = new Thread(new Test("test 1"));
Thread t2 = new Thread(new Test("test 2"));
t1.start();
t2.start();
}
}

上面创建了二个Test类的对象,相当于启动了两个线程, 这两个线程将会并发地执行它们的run方法中的代码。

MethodSync.java ,该类只拥有一个用来测试的 synchronized 方法

 public class MethodSync {

     /*
* @Task : 测试 synchronized 修饰方法时锁定的是调用该方法的对象
* @param name 线程的标记名称
*/
public synchronized void method(String name){
System.out.println(name + " Start a sync method");
try{
Thread.sleep(300);
}catch(InterruptedException e){}
System.out.println(name + " End the sync method");
}
}

先看执行结果:

test1 先于 test2 执行 同步方法,但是却后于 test2 结束。这里并没有达到互斥的效果!原因是:MethodSync是实例变量,每次创建一个Test对象就会创建一个MethodSync对象, synchronized 只会锁定调用method()方法的那个MethodSync对象,而这里创建的两个线程分别拥有两个不同的MethodSync对象,它们调用method方法时就没有互斥关系。

当把Test.java 中的MethodSync 变量 用 static 来修饰时,执行结果如下:

这里,正确实现了同步作用。原因如下:这里也创建了二个线程(Test 对象),但是每个Test对象共享MethodSync 变量,也即只有一个MethodSync 变量在两个线程中执行 method方法,这样两个线程在执行到method 方法这段代码时就会形成互斥。

2018.12.13更新:

记录一下我对synchronized关键字的理解:

1,相对于ReentrantLock而言,synchronized锁是重量级锁,重量级体现在活跃性差一点。synchronized锁是内置锁,意味着JVM能基于synchronized锁做一些优化:比如增加锁的粒度(锁粗化)、锁消除。

2,在synchronized锁上阻塞的线程是不可中断的:线程A获得了synchronized锁,当线程B也去获取synchronized锁时会被阻塞。而且,线程B无法被其他线程中断(不可中断的阻塞),而ReentrantLock锁能实现可中断的阻塞。

3,synchronized锁释放是自动的,当线程执行退出synchronized锁保护的同步代码块时,会自动释放synchronized锁。而ReentrantLock需要显示地释放:即在try-finally块中释放锁。

4,线程在竞争synchronized锁时是非公平的:假设synchronized锁目前被线程A占有,线程B请求锁未果,被放入队列中,线程C请求锁未果,也被 放入队列中,线程D也来请求锁,恰好此时线程A将锁释放了,那么线程D将跳过队列中所有的等待线程(即:线程B和线程C)并获得这个锁。

而ReentrantLock能够实现锁的公平性。

5,synchronized锁是读写互斥并且 读读也互斥,ReentrantReadWriteLock 分为读锁和写锁,而读锁可以同时被多个线程持有,适合于读多写少场景的并发。

JAVA 同步之 synchronized 修饰方法的更多相关文章

  1. java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?

    wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...

  2. synchronized修饰方法和对象的区别

    使用synchronized(object) { 代码块.... } 能对代码块进行加锁,不允许其他线程访问,其的作用原理是:在object内有一个变量,当有线程进入时,判断是否为0,如果为0,表示可 ...

  3. 关于java线程锁synchronized修饰普通方法与静态方法的区别

    最近研究线程方面问题,关于这个synchronized锁修饰的问题,先是修饰普通方法,然后通过两个线程,各自执行自己对象的锁,发现方法执行互不影响,代码如下: private static int n ...

  4. 一张图讲解对象锁和关键字synchronized修饰方法

    每个对象在出生的时候就有一把钥匙(监视器),那么被synchronized 修饰的方法相当于给方法加了一个锁,这个方法就可以进行同步,在多线程的时候,不会出现线程安全问题. 下面通过一张图片进行讲解: ...

  5. java同步代码(synchronized)中使用BlockingQueue

    说起BlockingQueue,大家最熟悉的就是生产者-消费者模式下的应用.但是如果在调用queue的上层代码加了同步块就会导致线程死锁. 例如: static BlockingQueue<St ...

  6. java基础之synchronized使用方法

    首先.參考文章:http://www.cnblogs.com/devinzhang/archive/2011/12/14/2287675.html PS:參考文章非常长,但内容非常丰富,若是刚開始学习 ...

  7. synchronized修饰方法和代码块的区别

    先看一段代码 Class A { public synchronized methodA() {//对当前对象加锁 } public methodB() { synchronized(this){}/ ...

  8. Java中synchronized 修饰在static方法和非static方法的区别

    [问题描述]关于Java中synchronized 用在实例方法和对象方法上面的区别 [问题分析]大家都知道,在Java中,synchronized 是用来表示同步的,我们可以synchronized ...

  9. JAVA之线程同步的三种方法

    最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...

随机推荐

  1. WCF-Oracel适配器针对UDT的使用配置与注意事项

    配置方法 1.针对Oracle UDT 的数据类型需要在开发过程中手动配置生成的DLL位置和Key位置,Visual Studio->添加生成项目->Add Adapter Metadat ...

  2. vue及ElementUI环境搭建

    1. nodejs安装及npm安装 下载地址:https://nodejs.org/en/download/ 选择windows Installer 下载完成后 运行node-v8.11.1-x64. ...

  3. xml序列化与反序列化工具

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  4. vim几种常用的插入模式

    我们来看一下cw这个命令,它是从光标处替换到后一个单词的结尾.比如说我的光标如图: 我的namespace写错了,后面的都对,就可以用cw命令,

  5. git学习小游戏

    学习git可以试试这个游戏:https://learngitbranching.js.org/

  6. HashMap中capacity、loadFactor、threshold、size等概念的解释<转>

    最近在看HashMap的源码,有很多概念都很模糊,今天写了一个测试例子,加深对这几个概念的理解,并演示了扩容及树化的过程(见下篇博文:). 注:本文基于JDK 1.8  HashMap的结构 约定 约 ...

  7. linux 查找并kill进程

    以php以关键字查找进程 $ ps aux | grep php root             32957   0.0  0.1  2470904   8908 s002  S+    4:53下 ...

  8. 2. ansible-playbook 条件语句-内部变量使用

    内部变量指的是把变量定义在playbook里面或者是执行结果作为变量 循环语句-标准Loops [root@LeoDevops playb]# cat p_loop.yaml - hosts: u12 ...

  9. python中的if __name__ == 'main'

    当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用: 对于编程语言来说,程序都必须要有一个入口,比如java和c#必须要 ...

  10. Mysql Window 解压版 忘记密码

    1. 首先检查mysql服务是否启动,若已启动则先将其停止服务,可在开始菜单的运行,使用命令: net stop mysql 打开第一个cmd1窗口,切换到mysql的bin目录,运行命令: mysq ...