Java synchronized 总结
在Java开发的时候经常会用到关键字synchronized来对代码进行同步,在使用的过程中,对于synchronized确不是很熟悉,最近在看Spring源码时,发现有不少地方都用到同步,因此,趁此机会,研究一下。
1. synchronized锁的对象
1)对于同步方法,锁是当前实例对象。
2)对于静态同步方法,锁是当前对象的Class对象。因为在Java 虚拟机中一个类只能对应一个类对象,所以同时只允许一个线程执行同一个类中的静态同步方法。
3)对于同步方法块,锁是Synchonized括号里配置的对象。
下面我们将一一根据代码来介绍这几种锁。
2. 同步实例方法
对于同步方法,锁是当前实例对象。即在同一时刻只能有一个线程可以访问该实例的同步方法。但是请注意,如果有多个实例对象,那么不同示例之间不受影响,线程可以同时访问不同示例的同步方法。看下面这段代码:
package com.bj.chenfeic.concurrency; /**
* 同步测试,提供了用于同步的实例方法
*
* @author chenfei0801
*
*/
public class Sync { public synchronized void testSync() {
long id = Thread.currentThread().getId();
System.out.println("线程:" + id + "进入同步块");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块");
}
}
package com.bj.chenfeic.concurrency; /**
*
* @author chenfei0801
*
*/
public class SyncTread implements Runnable { @Override
public void run() {
Sync sync = new Sync();
sync.testSync();
} }
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
for(int i=;i<;i++) {
newFixedThreadPool.execute(new SyncTread());
}
newFixedThreadPool.shutdown(); } }
执行结果:
线程:11进入同步块
线程:13进入同步块
线程:12进入同步块
线程:13退出同步块
线程:11退出同步块
线程:12退出同步块
从执行结果中,我们可以看到testSync并没有同步,synchronized没有生效。这是因为在SyncTherd中每次都是new Sync();这样synchronized其实是作用到不同的对象上去了,所以各个线程之间其实并没有同步。
按照如下修改SynsThread、SyncTest代码,即可达到同步作用
package com.bj.chenfeic.concurrency; /**
*
* @author chenfei0801
*
*/
public class SyncTread implements Runnable {
private Sync sync;
public SyncTread(Sync sync) {
this.sync = sync;
} @Override
public void run() {
this.sync.testSync();
} }
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
Sync sync = new Sync();//只实例化一次
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
for (int i = ; i < ; i++) {
newFixedThreadPool.execute(new SyncTread(sync));
}
newFixedThreadPool.shutdown(); } }
执行结果
线程:11进入同步块
线程:11退出同步块
线程:12进入同步块
线程:12退出同步块
线程:13进入同步块
线程:13退出同步块
从上面结果中可以看出,SyncThread中使用的是同一个对象,此时线程是互斥访问testSync()方法的,即达到了同步的作用。
3、同步静态方法
package com.bj.chenfeic.concurrency; /**
* 同步测试,提供了用于同步的实例方法
*
* @author chenfei0801
*
*/
public class Sync { //同步静态方法块
public synchronized static void testSync() {
long id = Thread.currentThread().getId();
System.out.println("线程:" + id + "进入同步块");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块");
}
}
package com.bj.chenfeic.concurrency; /**
*
* @author chenfei0801
*
*/
public class SyncTread implements Runnable { @Override
public void run() {
// Sync.testSync();
new Sync().testSync();//为了说明同步机制,我们此处先实例化对象。正常调用应该还是按照上行那样处理。
} }
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
for (int i = ; i < ; i++) {
newFixedThreadPool.execute(new SyncTread());
}
newFixedThreadPool.shutdown(); } }
执行结果:
线程:12进入同步块
线程:12退出同步块
线程:13进入同步块
线程:13退出同步块
线程:11进入同步块
线程:11退出同步块
从执行结果上,可以看到线程是同步访问代码块的。为了说明我们在SyncTread中每次都通过new的方式,实例化了新的对象。虽然在每个线程中,每个Sync对象都不相同,但是由于同步的是静态方法,因此所锁作用在所有Sync对象上。
除了将对整个方法加锁进行同步外,Java还支持对方法内部的方法快进行同步,这样做个人觉得有两个好处
1)一个方法可能还含有不需要同步的部分,而且有可能这个不需要同步的代码执行过程很耗时,这样就会影响其他线程执行,严重影响效率。
package com.bj.chenfeic.concurrency; /**
* 同步测试,提供了用于同步的实例方法
*
* @author chenfei0801
*
*/
public class Sync { public void testSync() {
long id = Thread.currentThread().getId();
synchronized (this) {
System.out.println("线程:" + id + "进入同步块");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块");
}
try {
Thread.sleep();//耗时操作,未同步
System.out.println("线程 "+ id +" 未同步部分");
} catch (InterruptedException e) {
e.printStackTrace();
} }
}
package com.bj.chenfeic.concurrency; /**
*
* @author chenfei0801
*
*/
public class SyncTread implements Runnable {
private Sync sync; public SyncTread(Sync sync) {
this.sync = sync;
} public SyncTread() {
this.sync = new Sync();//不传参数时每次实例化一个对象
} @Override
public void run() {
this.sync.testSync();
} }
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
Sync sync = new Sync();
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
for (int i = ; i < ; i++) {
newFixedThreadPool.execute(new SyncTread(sync));//同一个sync对象
}
newFixedThreadPool.shutdown(); } }
执行结果:
线程:11进入同步块
线程:11退出同步块
线程:13进入同步块
线程:13退出同步块
线程:12进入同步块
线程:12退出同步块
线程 未同步部分
线程 未同步部分
线程 未同步部分
从执行结果中可以看出,synchronized同步的代码块都是互斥访问的,对于耗时的操作则可以异步执行。提高了效率。注意的是,例子中synchronized后面括号中为this。说明锁是加在当前Sync实例对象上的。如果SyncTread中Sync对象不是同一个,则各个对象之间的代码不会同步。
上面的例子中,锁对象为当前的对象实例(this)。跟我们在“同步实例方法”中遇到一个同样的问题,如果每个线程都是重新实例化了一个对象,那么这些锁都会加在不同的实例对象上,这样此时每个对象之间都是互相独立的,这样就达不到同步的目的了。
修改SyncTest类的代码:
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
for (int i = ; i < ; i++) {
newFixedThreadPool.execute(new SyncTread());//没有传递任何参数
}
newFixedThreadPool.shutdown(); } }
SyncTread在实例化时如果没有传递参数,则每次都会实例化一个Sync对象。
运行结果如下:
线程:11进入同步块
线程:12进入同步块
线程:13进入同步块
线程:11退出同步块
线程:13退出同步块
线程:12退出同步块
线程 未同步部分
线程 未同步部分
线程 未同步部分
此时该如何处理?我们在“同步静态方法”中提到,静态方法之所以是全局的,是因为锁的对象不再是类的某个实例,而是整个类对象(Class)。因此我们在对方法块处理时,也可以同样的处理。
在上述代码的基础上修改Sync类
package com.bj.chenfeic.concurrency; /**
* 同步测试,提供了用于同步的实例方法
*
* @author chenfei0801
*
*/
public class Sync { public void testSync() {
long id = Thread.currentThread().getId();
synchronized (Sync.class) {//全局锁
System.out.println("线程:" + id + "进入同步块");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块");
}
try {
Thread.sleep();//耗时操作
System.out.println("线程 "+ id +" 未同步部分");
} catch (InterruptedException e) {
e.printStackTrace();
} }
}
执行结果为
线程:11进入同步块
线程:11退出同步块
线程:12进入同步块
线程:12退出同步块
线程:13进入同步块
线程:13退出同步块
线程 未同步部分
线程 未同步部分
线程 未同步部分
从执行结果上可以看出,各个线程虽然都实例化了各自的Sync对象,但是却是在互斥访问Sync中的同步块。
2)同步方法块,除了对可以减少同步区域,还可以减小锁的范围,在对方法进行同步时,同步的对象都是对象的实例或者类对象。如果一个类有多个同步方法,如果当前线程进入了其中一个方法里,这个类的其他方法此时也被锁住了,其他线程无法进入。一般情况下,可能没有问题,但是如果如果这些方法或者部分方法之间其实并没有什么关系,那么互斥访问显然会影响效率,看下面的例子:
package com.bj.chenfeic.concurrency; /**
* 同步测试,提供了用于同步的实例方法
*
* @author chenfei0801
*
*/
public class Sync { /** Synchronization monitor for the "active" flag 。仅仅用于同步*/
private final Object activeMonitor = new Object(); /** Synchronization monitor for the "refresh" and "destroy"仅仅用于同步 */
private final Object startupShutdownMonitor = new Object(); public void refresh() {
long id = Thread.currentThread().getId();
synchronized (this.startupShutdownMonitor) {
System.out.println("线程:" + id + "进入同步块--refresh()");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块-----refresh()");
} } public void destroy() {
long id = Thread.currentThread().getId();
synchronized (this.startupShutdownMonitor) {
System.out.println("线程:" + id + "进入同步块--destroy()");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块-----destroy()");
} } public void active() {
long id = Thread.currentThread().getId();
synchronized (this.activeMonitor) {
System.out.println("线程:" + id + "进入同步块--active()");
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + id + "退出同步块-----active()");
} } }
类Sync中有三个同步的方法,分别是refresh,destory,active。其中前两者是用的是同一个对象锁(Sync的startupShutdownMonitor对象),后者是另外的对象锁,现在我们看下测试的类。
package com.bj.chenfeic.concurrency; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
*
* @author chenfei0801
*
*/
public class SyncTest { /**
* @param args
*/
public static void main(String[] args) {
Sync sync = new Sync();
SyncTest syncTest = new SyncTest();
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool();
newFixedThreadPool.execute(syncTest.new ActiveThread(sync));
newFixedThreadPool.execute(syncTest.new RefreshThread(sync));
newFixedThreadPool.execute(syncTest.new DestoryThread(sync));
newFixedThreadPool.execute(syncTest.new ActiveThread(sync));
newFixedThreadPool.shutdown();
} class RefreshThread implements Runnable { private Sync sync; public RefreshThread(Sync sync) {
this.sync = sync;
} @Override
public void run() {
sync.refresh();
} } class DestoryThread implements Runnable { private Sync sync; public DestoryThread(Sync sync) {
this.sync = sync;
} @Override
public void run() {
sync.destroy();
} } class ActiveThread implements Runnable { private Sync sync; public ActiveThread(Sync sync) {
this.sync = sync;
} @Override
public void run() {
sync.active();
} } }
在测试类中,有3类线程,分布式调用Sync中的refreash,destory,active。在测试类中,启动了4个线程,我们看一下执行结果
线程:11进入同步块--active()
线程:13进入同步块--destroy()
线程:11退出同步块-----active()
线程:14进入同步块--active()
线程:13退出同步块-----destroy()
线程:12进入同步块--refresh()
线程:14退出同步块-----active()
线程:12退出同步块-----refresh()
从执行结果中,可以看出active之间是同步的,但是与destory、refresh之间却没有同步。线程11在在active方法时,线程13可以执行destory方法。
destory方法和refresh是同步的。
由此可见,通过对方法内部的方法快进行同步,可以灵活的决定同步块的范围和相应的锁对象。
最后,一定要记住synchronized锁住的是括号里的对象,而不是代码(方法或者方法快)
Java synchronized 总结的更多相关文章
- Java Synchronized Blocks
From http://tutorials.jenkov.com/java-concurrency/synchronized.html By Jakob Jenkov A Java synchro ...
- java synchronized(一)
java synchronized主要用于控制线程同步,中间有很多小的细节,知识,这里我简单的整理一下,做个记录.主要用于方法和代码块的控制 先说说方法控制 模拟银行存款和取款,创建一个Account ...
- java synchronized使用
java synchronized 基本上,所有并发的模式在解决线程冲突问题的时候,都是采用序列化共享资源的方案.这意味着在给定时刻只允许一个任务访问该资源.这个一般通过在代码上加一条锁语句实现,因为 ...
- Java synchronized 详解
Java synchronized 详解 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 1.当两个并发线程访问同一个对象object ...
- Java Synchronized 关键字
本文内容 Synchronized 关键字 示例 Synchronized 方法 内部锁(Intrinsic Locks)和 Synchronization 参考资料 下载 Demo Synchron ...
- Java synchronized 关键字详解
Java synchronized 关键字详解 前置技能点 进程和线程的概念 线程创建方式 线程的状态状态转换 线程安全的概念 synchronized 关键字的几种用法 修饰非静态成员方法 sync ...
- Java synchronized对象级别与类级别的同步锁
Java synchronized 关键字 可以将一个代码块或一个方法标记为同步代码块.同步代码块是指同一时间只能有一个线程执行的代码,并且执行该代码的线程持有同步锁.synchronized关键字可 ...
- java synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...
- Java synchronized
1. 将synchronized加在方法上, 即可实现对此方法的同步 public synchronized void deposit(float amt) { float tmp = amount; ...
- Java synchronized指南
在多线程程序中,同步修饰符用来控制对临界区代码的访问.其中一种方式是用synchronized关键字来保证代码的线程安全性.在Java中,synchronized修饰的代码块或方法不会被多个线程并发访 ...
随机推荐
- Android开发视频学习(2)
S02E05_Android当中的线程 Worker Thread不允许操作UI,只能在Main Thread操作UI S02E06_Handler(一) Handler,Looper,Message ...
- Alexander Grothendieck去世了
Alexander Grothendieck (German: [ˈɡroːtn̩diːk]; French: [ɡʁɔtɛndik]; 28 March 1928 – 13 November 201 ...
- HDU-1700 Points on Cycle
这题的俩种方法都是看别人的代码,方法可以学习学习,要多看看.. 几何题用到向量.. Points on Cycle Time Limit: 1000/1000 MS (Java/Others) ...
- win7下的vxworks总结
在visualbox下运行vxworks 先来看一张效果图: 在tornado端 成功运行第一个程序,输出了visualbox can run the vxworks ! 在vmware下的速度快多了 ...
- 2015美团网 哈工大 第k个排列
leetcode 上的Permutation Sequence 下面是可执行代码 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 以1 开头 123,132,共2!个数 2 开 ...
- public staic void main 总结
jvm 就是java的操作系统.深入了解jvm很必要. public:该函数的修饰符,表示该函数是公有的,无需多言. static 对于函数的修饰,表明该方法为静态方法,可以通过类名直接调用,事项对于 ...
- VS2012 C#调用C++ dll
VS2012 C#调用C++ dll 调试方法:[dll工程和调用dll的exe工程在同一个解决方案中]dll工程,属性-配置属性-调试-把 命令 为 调用该dll的exe工程的bin\Debug\* ...
- JUnit学习总结
Junit简介: Junit最初是由Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework),为单元测试(Unit Test) ...
- linux重定向总结:如何将shell命令的输出信息自动输出到文件中保存
在做批量实验室,例如跑批量MR的作业,我们会写好shell脚本,然后启动脚本,等所有作业执行完再去看结果,但是这些执行时的信息如何保存下来到文件中呢?下面这个命令可以完成这个任务. sh batchj ...
- LeetCode:Restore IP Address
93. Restore IP Addresses Given a string containing only digits, restore it by returning all possible ...