多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,调用的效果就是按照顺序执行,也就是同步,阻塞的。
这说明synchronized同步方法和synchronized(this)同步代码块分别有两种作用。
a. synchronized同步方法
(1)对其他同步方法或者同步代码块调用呈阻塞状态。
(2)同一时间只有一个线程可以执行synchronized同步方法中的代码。
b.synchronized同步代码块
(1)对其他同步方法或者同步代码块调用呈阻塞状态。
(2)同一时间只有一个线程可以执行synchronized同步方法中的代码。
使用synchronized(this)格式来同步代码块,其实还可以用任意对象作为对象监视器来实现同步。这个任意对象大多数是实例变量及方法中的参数,
使用格式为synchronized(非this对象)
a:在多个线程中持有对象监视器作为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
b:当持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
如下验证a
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class Service { private String usrName; private String pwd; private String anyString = new String(); public void setUsrNameAndPwd(String usrName, String pwd) { try { synchronized (anyString) { System.out.println("线程名:"+Thread.currentThread().getName() +"进入同步代码块"); Thread.sleep(2000); System.out.println("线程名:"+Thread.currentThread().getName() +"离开同步代码块"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.setUsrNameAndPwd("a", "aa"); } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.setUsrNameAndPwd("b", "bb"); } }
package com.cky.test; import com.cky.thread.ThreadA; import com.cky.thread.ThreadB; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/6. */ public class Test2 { public static void main(String[] args) throws InterruptedException { Service task = new Service(); ThreadA threadA = new ThreadA(task); threadA.start(); ThreadB threadB = new ThreadB(task); threadB.start(); } }
D:itjdk1.8injava -Didea.launcher.port=7537 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib esources.jar;D:itjdk1.8jrelib t.jar;F:springboot hreaddemooutproduction hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2 线程名:Thread-0进入同步代码块 线程名:Thread-0离开同步代码块 线程名:Thread-1进入同步代码块 线程名:Thread-1离开同步代码块
结果分析:
锁非this对象具有一定的优点如果在一个类中有很多个synchronized方法,那么虽然能实现同步,但是会受到阻塞,所以影响执行效率,但如果使用同步代码块非this对象
则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高效率
更改Service类中的代码(将anyString放入方法中)
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class Service { private String usrName; private String pwd; public void setUsrNameAndPwd(String usrName, String pwd) { try { String anyString = new String(); synchronized (anyString) { System.out.println("线程名:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis() +"进入同步代码块"); Thread.sleep(2000); System.out.println("线程名:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis() +"离开同步代码块"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
D:itjdk1.8injava -Didea.launcher.port=7538 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib esources.jar;D:itjdk1.8jrelib t.jar;F:springboot hreaddemooutproduction hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2 线程名:Thread-0在1512646079651进入同步代码块 线程名:Thread-1在1512646079652进入同步代码块 线程名:Thread-0在1512646081652离开同步代码块 线程名:Thread-1在1512646081652离开同步代码块
如何不是同步执行,而是异步交叉执行的,因为对象监视器不是同一个对象,所以异步交叉调用。
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class Service { private String anyString = new String(); public void a() { try { synchronized (anyString) { System.out.println("a begin"); Thread.sleep(2000); System.out.println("a end"); } } catch (InterruptedException e) { e.printStackTrace(); } } public void b() { System.out.println("b begin"); System.out.println("b end"); } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.a(); } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.b(); } }
package com.cky.test; import com.cky.thread.ThreadA; import com.cky.thread.ThreadB; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/6. */ public class Test2 { public static void main(String[] args) throws InterruptedException { Service task = new Service(); ThreadA threadA = new ThreadA(task); threadA.start(); ThreadB threadB = new ThreadB(task); threadB.start(); } }
D:itjdk1.8injava -Didea.launcher.port=7539 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
esources.jar;D:itjdk1.8jrelib
t.jar;F:springboot hreaddemooutproduction hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
a begin
b begin
b end
a end
结果分析:
因为对象监视器的不同,所以运行结果是异步的。
同步代码块放在非同步方法中进行声明,并不能保证调用方法的线程的执行顺序是同步的,容易引发脏读。
先看如下例子
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class Service { synchronized public void a(String str) { try { System.out.println("name=" + Thread.currentThread().getName() + "执行了a方法"); Thread.sleep(2000); System.out.println("name=" + Thread.currentThread().getName() + "退出了a方法"); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void b() { System.out.println("b begin"); System.out.println("b end"); } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { super.run(); for (int i = 0; i < 100000; i++) { service.a("threadA"+ (i+1)); } } }
package com.cky.thread; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { super.run(); for (int i = 0; i < 100000; i++) { service.a("threadB"+ (i+1)); } } }
package com.cky.test; import com.cky.thread.ThreadA; import com.cky.thread.ThreadB; import com.cky.utils.Service; /** * Created by chenkaiyang on 2017/12/6. */ public class Test2 { public static void main(String[] args) throws InterruptedException { Service task = new Service(); ThreadA threadA = new ThreadA(task); threadA.start(); ThreadB threadB = new ThreadB(task); threadB.start(); } }
name=Thread-1执行了a方法 name=Thread-1退出了a方法 name=Thread-0执行了a方法 name=Thread-0退出了a方法 name=Thread-0执行了a方法 name=Thread-0退出了a方法 name=Thread-0执行了a方法 name=Thread-0退出了a方法
从结果分析:
同步代码块中的代码是同步打印的,当前执行和退出是成对出现的,但是线程A和线程B是异步执行的,这有可能造成脏读,因为顺序不确定。
下面演示出现脏读的例子,
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class MyService { public MyList addServiceMethod(MyList list, String data) { try { if (list.getSize() <1) { Thread.sleep(2000);//模拟从远程花费2秒 list.add(data); } } catch (InterruptedException e) { e.printStackTrace(); } return list; } }
package com.cky.utils; import java.util.ArrayList; import java.util.List; /** * Created by chenkaiyang on 2017/12/7. */ public class MyList { private List list = new ArrayList(); synchronized public void add(String str) { list.add(str); } synchronized public int getSize() { return list.size(); } }
package com.cky.thread; import com.cky.utils.MyList; import com.cky.utils.MyService; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadA extends Thread{ private MyList list; public ThreadA(MyList list) { super(); this.list = list; } @Override public void run() { super.run(); MyService myService = new MyService(); myService.addServiceMethod(list, "a"); } }
package com.cky.thread; import com.cky.utils.MyList; import com.cky.utils.MyService; /** * Created by chenkaiyang on 2017/12/5. */ public class ThreadB extends Thread{ private MyList list; public ThreadB(MyList list) { super(); this.list = list; } @Override public void run() { super.run(); MyService myService = new MyService(); myService.addServiceMethod(list, "b"); } }
package com.cky.test; import com.cky.thread.ThreadA; import com.cky.thread.ThreadB; import com.cky.utils.MyList; /** * Created by chenkaiyang on 2017/12/6. */ public class Test2 { public static void main(String[] args) throws InterruptedException { MyList myList = new MyList(); ThreadA threadA = new ThreadA(myList); threadA.start(); ThreadB threadB = new ThreadB(myList); threadB.start(); Thread.sleep(5000); System.out.println(myList.getSize()); } }
D:itjdk1.8injava -Didea.launcher.port=7533 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
esources.jar;D:itjdk1.8jrelib
t.jar;F:springboot hreaddemooutproduction hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
2
上面出现了脏读:两个线程以异步的方式返回list参数的size大小,解决方法就是同步化。
package com.cky.utils; /** * Created by chenkaiyang on 2017/12/7. */ public class MyService { public MyList addServiceMethod(MyList list, String data) { try { synchronized (list) { if (list.getSize() <1) { Thread.sleep(2000);//模拟从远程花费2秒 list.add(data); } } } catch (InterruptedException e) { e.printStackTrace(); } return list; } }
D:itjdk1.8injava -Didea.launcher.port=7534 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
esources.jar;D:itjdk1.8jrelib
t.jar;F:springboot hreaddemooutproduction hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
1
由于list参数对象在项目中实一份单例,也正需要对list参数的getSize方法做同步的调用。