sychronized锁代码块
来源:互联网 发布:音乐打谱软件 编辑:程序博客网 时间:2024/05/22 10:51
synchronized同步代码块
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用synchronized同步语句块来解决问题。看一下例子:
public class ThreadDomain18{ public void doLongTimeTask() throws Exception { for (int i = 0; i < 100; i++) { System.out.println("nosynchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1)); } System.out.println(); synchronized (this) { for (int i = 0; i < 100; i++) { System.out.println("synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1)); } } }}
public class MyThread18 extends Thread{ private ThreadDomain18 td; public MyThread18(ThreadDomain18 td) { this.td = td; } public void run() { try { td.doLongTimeTask(); } catch (Exception e) { e.printStackTrace(); } }}
public static void main(String[] args){ ThreadDomain18 td = new ThreadDomain18(); MyThread18 mt0 = new MyThread18(td); MyThread18 mt1 = new MyThread18(td); mt0.start(); mt1.start();}
运行结果,分两部分来看:
synchronized threadName = Thread-1, i = 1synchronized threadName = Thread-1, i = 2nosynchronized threadName = Thread-0, i = 95synchronized threadName = Thread-1, i = 3nosynchronized threadName = Thread-0, i = 96synchronized threadName = Thread-1, i = 4nosynchronized threadName = Thread-0, i = 97synchronized threadName = Thread-1, i = 5nosynchronized threadName = Thread-0, i = 98synchronized threadName = Thread-1, i = 6nosynchronized threadName = Thread-0, i = 99synchronized threadName = Thread-1, i = 7nosynchronized threadName = Thread-0, i = 100
...synchronized threadName = Thread-1, i = 98synchronized threadName = Thread-1, i = 99synchronized threadName = Thread-1, i = 100synchronized threadName = Thread-0, i = 1synchronized threadName = Thread-0, i = 2synchronized threadName = Thread-0, i = 3...
这个实验可以得出以下两个结论:
1、当A线程访问对象的synchronized代码块的时候,B线程依然可以访问对象方法中其余非synchronized块的部分,第一部分的执行结果证明了这一点
2、当A线程进入对象的synchronized代码块的时候,B线程如果要访问这段synchronized块,那么访问将会被阻塞,第二部分的执行结果证明了这一点
所以,从执行效率的角度考虑,有时候我们未必要把整个方法都加上synchronized,而是可以采取synchronized块的方式,对会引起线程安全问题的那一部分代码进行synchronized就可以了。
两个synchronized块之间具有互斥性
如果线程1访问了一个对象A方法的synchronized块,那么线程B对同一对象B方法的synchronized块的访问将被阻塞,写个例子来证明一下:
public class ThreadDomain19{ public void serviceMethodA() { synchronized (this) { try { System.out.println("A begin time = " + System.currentTimeMillis()); Thread.sleep(2000); System.out.println("A end time = " + System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } public void serviceMethodB() { synchronized (this) { System.out.println("B begin time = " + System.currentTimeMillis()); System.out.println("B end time = " + System.currentTimeMillis()); } }}
写两个线程分别调用这两个方法:
public class MyThread19_0 extends Thread{ private ThreadDomain19 td; public MyThread19_0(ThreadDomain19 td) { this.td = td; } public void run() { td.serviceMethodA(); }}
public class MyThread19_1 extends Thread{ private ThreadDomain19 td; public MyThread19_1(ThreadDomain19 td) { this.td = td; } public void run() { td.serviceMethodB(); }}
写个main函数:
public static void main(String[] args){ ThreadDomain19 td = new ThreadDomain19(); MyThread19_0 mt0 = new MyThread19_0(td); MyThread19_1 mt1 = new MyThread19_1(td); mt0.start(); mt1.start();}
看一下运行结果:
A begin time = 1443843271982A end time = 1443843273983B begin time = 1443843273983B end time = 1443843273983
看到对于serviceMethodB()方法synchronized块的访问必须等到对于serviceMethodA()方法synchronized块的访问结束之后。那其实这个例子,我们也可以得出一个结论:synchronized块获得的是一个对象锁,换句话说,synchronized块锁定的是整个对象。
synchronized块和synchronized方法
既然上面得到了一个结论synchronized块获得的是对象锁,那么如果线程1访问了一个对象方法A的synchronized块,线程2对于同一对象同步方法B的访问应该是会被阻塞的,因为线程2访问同一对象的同步方法B的时候将会尝试去获取这个对象的对象锁,但这个锁却在线程1这里。写一个例子证明一下这个结论:
public class ThreadDomain20{ public synchronized void otherMethod() { System.out.println("----------run--otherMethod"); } public void doLongTask() { synchronized (this) { for (int i = 0; i < 1000; i++) { System.out.println("synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1)); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } }}
写两个线程分别调用这两个方法:
public class MyThread20_0 extends Thread{ private ThreadDomain20 td; public MyThread20_0(ThreadDomain20 td) { this.td = td; } public void run() { td.doLongTask(); }}
public class MyThread20_1 extends Thread{ private ThreadDomain20 td; public MyThread20_1(ThreadDomain20 td) { this.td = td; } public void run() { td.otherMethod(); }}
写个main函数调用一下,这里"mt0.start()"后sleep(100)以下是为了确保mt0线程先启动:
public static void main(String[] args) throws Exception { ThreadDomain20 td = new ThreadDomain20(); MyThread20_0 mt0 = new MyThread20_0(td); MyThread20_1 mt1 = new MyThread20_1(td); mt0.start(); Thread.sleep(100); mt1.start(); }
看一下运行结果:
...synchronized threadName = Thread-0, i = 995synchronized threadName = Thread-0, i = 996synchronized threadName = Thread-0, i = 997synchronized threadName = Thread-0, i = 998synchronized threadName = Thread-0, i = 999synchronized threadName = Thread-0, i = 1000----------run--otherMethod
证明了我们的结论。为了进一步完善这个结论,把"otherMethod()"方法的synchronized去掉再看一下运行结果:
...synchronized threadName = Thread-0, i = 16synchronized threadName = Thread-0, i = 17synchronized threadName = Thread-0, i = 18synchronized threadName = Thread-0, i = 19synchronized threadName = Thread-0, i = 20----------run--otherMethodsynchronized threadName = Thread-0, i = 21synchronized threadName = Thread-0, i = 22synchronized threadName = Thread-0, i = 23...
"otherMethod()"方法和"doLongTask()"方法中的synchronized块异步执行了
将任意对象作为对象监视器
总结一下前面的内容:
1、synchronized同步方法
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized同步方法中的代码
2、synchronized同步代码块
(1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态
(2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
前面都使用synchronized(this)的格式来同步代码块,其实Java还支持对"任意对象"作为对象监视器来实现同步的功能。这个"任意对象"大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:
public class ThreadDomain21{ private String userNameParam; private String passwordParam; private String anyString = new String(); public void setUserNamePassword(String userName, String password) { try { synchronized (anyString) { System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 进入同步代码块"); userNameParam = userName; Thread.sleep(3000); passwordParam = password; System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 离开同步代码块"); } } catch (InterruptedException e) { e.printStackTrace(); } }}
写两个线程分别调用一下:
public class MyThread21_0 extends Thread{ private ThreadDomain21 td; public MyThread21_0(ThreadDomain21 td) { this.td = td; } public void run() { td.setUserNamePassword("A", "AA"); }}
public class MyThread21_1 extends Thread{ private ThreadDomain21 td; public MyThread21_1(ThreadDomain21 td) { this.td = td; } public void run() { td.setUserNamePassword("B", "B"); }}
写一个main函数调用一下:
public static void main(String[] args){ ThreadDomain21 td = new ThreadDomain21(); MyThread21_0 mt0 = new MyThread21_0(td); MyThread21_1 mt1 = new MyThread21_1(td); mt0.start(); mt1.start();}
看一下运行结果:
线程名称为:Thread-0在 1443855101706 进入同步代码块线程名称为:Thread-0在 1443855104708 离开同步代码块线程名称为:Thread-1在 1443855104708 进入同步代码块线程名称为:Thread-1在 1443855107708 离开同步代码块
这个例子证明了:多个线程持有"对象监视器"为同一个对象的前提下,同一时间只能有一个线程可以执行synchronized(非this对象x)代码块中的代码。
锁非this对象具有一定的优点:如果在一个类中有很多synchronized方法,这时虽然能实现同步,但会受到阻塞,从而影响效率。但如果同步代码块锁的是非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高了运行效率。
注意一下"private String anyString = new String();"这句话,现在它是一个全局对象,因此监视的是同一个对象。如果移到try里面,那么对象的监视器就不是同一个了,调用的时候自然是异步调用,可以自己试一下。
最后提一点,synchronized(非this对象x),这个对象如果是实例变量的话,指的是对象的引用,只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的。
细化synchronized(非this对象x)的三个结论
synchronized(非this对象x)格式的写法是将x对象本身作为对象监视器,有三个结论得出:
1、当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
2、当其他线程执行x对象中的synchronized同步方法时呈同步效果
3、当其他线程执行x对象方法中的synchronized(this)代码块时也呈同步效果
第一点很明显,第二点和第三点意思类似,无非一个是同步方法,一个是同步代码块罢了,举个例子验证一下第二点:
public class MyObject{ public synchronized void speedPrintString() { System.out.println("speedPrintString__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName()); System.out.println("----------"); System.out.println("speedPrintString__releaseLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName()); }}
ThreadDomain24中持有MyObject的引用:
public class ThreadDomain24{ public void testMethod1(MyObject mo) { try { synchronized (mo) { System.out.println("testMethod1__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("testMethod1__releaseLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } }}
写两个线程分别调用"speedPrintString()"方法和"testMethod1(MyObject mo)"方法:
public class MyThread24_0 extends Thread{ private ThreadDomain24 td; private MyObject mo; public MyThread24_0(ThreadDomain24 td, MyObject mo) { this.td = td; this.mo = mo; } public void run() { td.testMethod1(mo); }}
public class MyThread24_1 extends Thread{ private MyObject mo; public MyThread24_1(MyObject mo) { this.mo = mo; } public void run() { mo.speedPrintString(); }}
写一个main函数启动这两个线程:
public static void main(String[] args){ ThreadDomain24 td = new ThreadDomain24(); MyObject mo = new MyObject(); MyThread24_0 mt0 = new MyThread24_0(td, mo); MyThread24_1 mt1 = new MyThread24_1(mo); mt0.start(); mt1.start();}
看一下运行结果:
testMethod1__getLock time = 1443855939811, run ThreadName = Thread-0testMethod1__releaseLock time = 1443855944812, run ThreadName = Thread-0speedPrintString__getLock time = 1443855944812, run ThreadName = Thread-1----------speedPrintString__releaseLock time = 1443855944812, run ThreadName = Thread-1
看到"speedPrintString()"方法必须等待"testMethod1(MyObject mo)"方法执行完毕才可以执行,没有办法异步执行,证明了第二点的结论。第三点的验证方法类似,就不写代码证明了。
- sychronized锁代码块
- Java同步机制:sychronized对代码影响
- Java同步机制:sychronized对代码影响
- 锁代码块
- 关于sychronized锁的一些研究。
- 关于java中的 sychronized 同步方法 与 同步块的理解
- 关于java中的 sychronized 同步方法 与 同步块的理解 .
- 关于java中的 sychronized 同步方法 与 同步块的理解
- 关于java中的 sychronized 同步方法 与 同步块的理解
- 同步代码块,同步代码方法,锁
- 代码块
- 代码块
- 代码块
- 代码块
- 代码块
- 代码块
- 代码块
- 代码块
- Openstack虚拟机内安装docker,容器无法联网问题
- spring-IOC
- 字符串中"=="和"equals()"&NULL和""问题
- gulp初探
- sublime2安装less
- sychronized锁代码块
- iOS 正确选择图片加载方式
- atitit.userService 用户系统设计 v6 q413
- Xcode中编译报错
- 解决浏览器访问跨源问题
- CocoaPods详解之----制作篇
- jax-rs中出现的问题
- NodeJS研究笔记:利用Buffer类的二进制数据读取接口解析ELF文件格式
- android事件分发机制之ViewGroup篇,简化版