一、同步代码块锁this
(一)同步方法时
synchronized声明方法在有些情况下是有弊端的,比如A线程调用同步方法执行一段很长的一段时间,
则B线程一直在等待,这样使用synchronized代码块是对某一个对象进行加锁。
package com.it.po.thread05; public class Task1 { private String getData1; private String getData2; synchronized public void doLongTimeTask(){ try { System.out.println("线程:"+Thread.currentThread().getName()+" begin task"); Thread.sleep(3000); System.out.println("执行长时间后获取数据返回。。。getData1 和getData2 "); System.out.println("线程:"+Thread.currentThread().getName()+" end task"); getData1 = "获取数据返回 getData1"; getData2 = "获取数据返回 getData2"; } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class MyThread01_1 extends Thread{ private Task1 task1; public MyThread01_1(Task1 task1) { this.task1 = task1; } @Override public void run() { super.run(); task1.doLongTimeTask(); } }
package com.it.po.thread05; public class MyThread01_2 extends Thread{ private Task1 task1; public MyThread01_2(Task1 task1) { this.task1 = task1; } @Override public void run() { super.run(); task1.doLongTimeTask(); } }
package com.it.po.thread05; import com.it.po.thread04.GetNum; import com.it.po.thread04.MyThread01_1; import com.it.po.thread04.MyThread01_2; import com.it.po.thread04.MyThread9_1; public class Run01 { public static void main(String[] args){ Task1 task1 = new Task1(); com.it.po.thread05.MyThread01_1 my1 = new com.it.po.thread05.MyThread01_1(task1); com.it.po.thread05.MyThread01_2 my2 = new com.it.po.thread05.MyThread01_2(task1); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程:A begin task
执行长时间后获取数据返回。。。
线程:A end task
线程:B begin task
执行长时间后获取数据返回。。。
线程:B end task
(二)、同步代码块解决同步方法的弊端
package com.it.po.thread05; public class Task2 { private String getData1; private String getData2; public void doLongTimeTask(){ try { System.out.println("线程:"+Thread.currentThread().getName()+" begin task"); Thread.sleep(3000); System.out.println("执行长时间后获取数据返回 getData1 和getData2.。。"); System.out.println("线程:"+Thread.currentThread().getName()+" end task"); synchronized (this) { getData1 = Thread.currentThread().getName()+ " 获取数据 getData1"; getData2 = Thread.currentThread().getName()+" 获取数据 getData2"; System.out.println("getData1= "+getData1); System.out.println("getData2= "+getData2); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class MyThread02_1 extends Thread{ private Task2 task2; public MyThread02_1(Task2 task2) { this.task2 = task2; } @Override public void run() { super.run(); task2.doLongTimeTask(); } }
package com.it.po.thread05; public class MyThread02_2 extends Thread{ private Task2 task2; public MyThread02_2(Task2 task2) { this.task2 = task2; } @Override public void run() { super.run(); task2.doLongTimeTask(); } }
package com.it.po.thread05; public class Run02 { public static void main(String[] args){ Task2 task2 = new Task2(); com.it.po.thread05.MyThread02_1 my1 = new com.it.po.thread05.MyThread02_1(task2); com.it.po.thread05.MyThread02_2 my2 = new com.it.po.thread05.MyThread02_2(task2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程:B begin task 线程:A begin task 执行长时间后获取数据返回 getData1 和getData2.。。 执行长时间后获取数据返回 getData1 和getData2.。。 线程:B end task 线程:A end task getData1= B 获取数据 getData1 getData2= B 获取数据 getData2 getData1= A 获取数据 getData1 getData2= A 获取数据 getData2
结论1
一半异步一半同步
不在 synchronized执行就是异步,在 synchronized中执行就是同步,如上
红色部分异步,下方同步。
二、 synchronized(this)锁的是当前对象。
package com.it.po.thread05; public class ObjectService { synchronized public void methodA(){ System.out.println("brgin time= "+System.currentTimeMillis()+" methodA...."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end time= "+System.currentTimeMillis()+" methodA...."); } public void methodB(){ synchronized (this) { System.out.println("brgin time= " + System.currentTimeMillis() + " methodB...."); System.out.println("end time= " + System.currentTimeMillis() + " methodB...."); } } }
package com.it.po.thread05; public class MyThread03_1 extends Thread{ private ObjectService objectService; public MyThread03_1( ObjectService objectService) { this.objectService = objectService; } @Override public void run() { super.run(); objectService.methodA(); } }
package com.it.po.thread05; public class MyThread03_2 extends Thread{ private ObjectService objectService; public MyThread03_2(ObjectService objectService) { this.objectService = objectService; } @Override public void run() { super.run(); objectService.methodB(); } }
package com.it.po.thread05; public class Run03 { public static void main(String[] args){ ObjectService service = new ObjectService(); MyThread03_1 my1 = new MyThread03_1(service); MyThread03_2 my2 = new MyThread03_2(service); my1.start(); my2.start(); } }
brgin time= 1573870649146 methodA.... end time= 1573870650147 methodA.... brgin time= 1573870650148 methodB.... end time= 1573870650148 methodB....
三、任意对象作为对象对象监视器。
1、synchronized同步方法
1)对其他synchronized 同步方法或是 synchronized(this)同步代码块调用呈阻塞状态。
2)同一时间,只有一个线程可以执行 synchronized 同步方法中的代码。
2、synchronized(this)同步代码块
1)对其他synchronized 同步方法或是 synchronized(this)同步代码块调用呈阻塞状态。
2)同一时间,只有一个线程可以执行 synchronized 同步代码块中的代码。
此外
监视器除了可以用this,还可以用任意对象。
这个任意对象大多数是实例变量及方法参数。
(一)、实例变量作为对象监视器
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class Service1 { private String username; private String password; private List<String> list = new ArrayList<>() ; // private Object object=new Object(); // private String po= new String(); public void methodA( String username, String password){ try { synchronized (list){ System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。"); this.username=username; Thread.sleep(2000); this.password=password; System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class MyThread04_1 extends Thread{ private Service1 service1; public MyThread04_1(Service1 service1) { this.service1 = service1; } @Override public void run() { super.run(); // new Service1().methodA("a","aa"); service1.methodA("a","aa"); } }
package com.it.po.thread05; public class MyThread04_2 extends Thread{ private Service1 service1; public MyThread04_2(Service1 service1) { this.service1 = service1; } @Override public void run() { super.run(); // new Service1().methodA("b","bb"); service1.methodA("b","bb"); } }
package com.it.po.thread05; public class Run04 { public static void main(String[] args){ Service1 service1 = new Service1(); MyThread04_1 my1 = new MyThread04_1(service1); MyThread04_2 my2 = new MyThread04_2(service1); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程 A 进来了。。
线程 A 出去了。。
线程 B 进来了。。
线程 B 出去了。。
注意:此处虽然 synchronized (list) ,但是Service1必须是一个对象,否则就是多个list,无法锁住的。
此处,还可以将 private List<String> list = new ArrayList<>() ;换成如下。
private Object object=new Object();
private String po= new String();
(二)、特殊的情况字符常量作为监视器时
上面的例子修改如下
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class Service1 { private String username; private String password; private List<String> list = new ArrayList<>() ; // private Object object=new Object(); // private String po= new String(); private String lan="pp"; public void methodA( String username, String password){ try { synchronized (lan){ System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。"); this.username=username; Thread.sleep(2000); this.password=password; System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class Run04 { public static void main(String[] args){ Service1 service1 = new Service1(); Service1 service2 = new Service1(); MyThread04_1 my1 = new MyThread04_1(service1); MyThread04_2 my2 = new MyThread04_2(service2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程 B 进来了。。
线程 B 出去了。。
线程 A 进来了。。
线程 A 出去了。。
虽然两个对象service1 和service2是不一样的,但是字符常量lan="pp" 是一份,也是可以锁住的。
(三)对象监视器在方法内
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class Service2 { private String username; private String password; public void methodA( String username, String password){ List<String> list = new ArrayList<>() ; // Object object=new Object(); // String po= new String(); try { synchronized (list){ System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。"); this.username=username; Thread.sleep(2000); this.password=password; System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class MyThread05_1 extends Thread{ private Service2 service2; public MyThread05_1(Service2 service2) { this.service2 = service2; } @Override public void run() { super.run(); // new Service1().methodA("a","aa"); service2.methodA("a","aa"); } }
package com.it.po.thread05; public class MyThread05_2 extends Thread{ private Service2 service2; public MyThread05_2(Service2 service2) { this.service2 = service2; } @Override public void run() { super.run(); // new Service1().methodA("a","aa"); service2.methodA("a","aa"); } }
package com.it.po.thread05; public class Run05 { public static void main(String[] args){ Service2 service2 = new Service2(); MyThread05_1 my1 = new MyThread05_1(service2); MyThread05_2 my2 = new MyThread05_2(service2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程 A 进来了。。
线程 B 进来了。。
线程 A 出去了。。
线程 B 出去了。。
注意:方法内是线程安全的,多线程互不干扰
(四)对象监视器在方法内时实现同步效果
修改如下
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class Service2 { private String username; private String password; public void methodA( String username, String password){ // List<String> list = new ArrayList<>() ; // Object object=new Object(); //String po= new String(); String lu= "pp"; try { synchronized (lu){ System.out.println("线程 "+Thread.currentThread().getName()+" 进来了。。"); this.username=username; Thread.sleep(2000); this.password=password; System.out.println("线程 "+Thread.currentThread().getName()+" 出去了。。"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
线程 B 进来了。。
线程 B 出去了。。
线程 A 进来了。。
线程 A 出去了。。
对象监视器不同运行的结果就是异步的。
(五)循环添加数据可能出现的脏读
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class MyList1 { private List list =new ArrayList(); synchronized public void add(String name){ System.out.println("线程 :"+Thread.currentThread().getName()+" 进入执行了add方法"); list.add(name); System.out.println("线程 :"+Thread.currentThread().getName()+" 出去add方法"); } synchronized public int getSize(){ System.out.println("线程 :"+Thread.currentThread().getName()+" 进入 执行了getSize方法"); int size = list.size(); System.out.println("线程 :"+Thread.currentThread().getName()+" 出去getSize方法"); return size; } }
package com.it.po.thread05; public class MyThread06_1 extends Thread{ private MyList1 myList1; public MyThread06_1(MyList1 myList1) { this.myList1 = myList1; } @Override public void run() { super.run(); for(int i=0;i<100;i++){ myList1.add("MyThread06_1 "+i); } } }
package com.it.po.thread05; public class MyThread06_2 extends Thread{ private MyList1 myList1; public MyThread06_2(MyList1 myList1) { this.myList1 = myList1; } @Override public void run() { super.run(); for(int i=0;i<100;i++){ myList1.add("MyThread06_2 "+i); } } }
package com.it.po.thread05; public class Run06 { public static void main(String[] args){ MyList1 myList1 = new MyList1(); MyThread06_1 my1 = new MyThread06_1(myList1); MyThread06_2 my2 = new MyThread06_2(myList1); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
同步块的代码虽然是同步打印的,但是线程A和线程B确是异步的,这就有可能出现脏读的情况。
(六)重现脏读的情况
package com.it.po.thread05; import java.util.ArrayList; import java.util.List; public class MyList2 { private List list =new ArrayList(); synchronized public void add(String name){ System.out.println("线程 :"+Thread.currentThread().getName()+" 进入执行了add方法"); list.add(name); System.out.println("线程 :"+Thread.currentThread().getName()+" 出去add方法"); } synchronized public int getSize(){ System.out.println("线程 :"+Thread.currentThread().getName()+" 进入 执行了getSize方法"); int size = list.size(); System.out.println("线程 :"+Thread.currentThread().getName()+" 出去getSize方法"); return size; } }
package com.it.po.thread05; public class MyService1 { public MyList2 addServiceMethod(MyList2 list2,String data) { try { if(list2.getSize()<1){ Thread.sleep(2000);//模拟从远端获取数据话费2s list2.add(data); } } catch (InterruptedException e) { e.printStackTrace(); } return list2; } }
package com.it.po.thread05; public class MyThread07_1 extends Thread{ private MyList2 myList2; public MyThread07_1(MyList2 myList2) { this.myList2 = myList2; } @Override public void run() { super.run(); MyService1 myService1 = new MyService1(); myService1.addServiceMethod(myList2,"A"); } }
package com.it.po.thread05; public class MyThread07_2 extends Thread{ private MyList2 myList2; public MyThread07_2(MyList2 myList2) { this.myList2 = myList2; } @Override public void run() { super.run(); MyService1 myService1 = new MyService1(); myService1.addServiceMethod(myList2,"B"); } }
package com.it.po.thread05; public class Run07 { public static void main(String[] args){ try { MyList2 myList2 = new MyList2(); MyThread07_1 my1 = new MyThread07_1(myList2); MyThread07_2 my2 = new MyThread07_2(myList2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); Thread.sleep(3000); System.out.println("myList2大小:"+myList2.getSize()); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程 :B 进入 执行了getSize方法 线程 :B 出去getSize方法 线程 :A 进入 执行了getSize方法 线程 :A 出去getSize方法 线程 :B 进入执行了add方法 线程 :B 出去add方法 线程 :A 进入执行了add方法 线程 :A 出去add方法 线程 :main 进入 执行了getSize方法 线程 :main 出去getSize方法 myList2大小:2
两个线程停留在if判断那里
避免脏读
package com.it.po.thread05; public class MyService1 { public MyList2 addServiceMethod(MyList2 list2,String data) { synchronized (list2) { try { if (list2.getSize() < 1) { Thread.sleep(2000);//模拟从远端获取数据话费2s list2.add(data); } } catch (InterruptedException e) { e.printStackTrace(); } } return list2; } }
线程 :B 进入 执行了getSize方法
线程 :B 出去getSize方法
线程 :B 进入执行了add方法
线程 :B 出去add方法
线程 :A 进入 执行了getSize方法
线程 :A 出去getSize方法
线程 :main 进入 执行了getSize方法
线程 :main 出去getSize方法
myList2大小:1
总结:
1.当多个线程同时执行 synchronized同步代码块时,呈同步效果。
2.当其他线程执行X对象中 synchronized同步方法时呈同步效果。
package com.it.po.thread05; public class MyObject { synchronized public void myObjectMethod(){ try { System.out.println("线程 "+Thread.currentThread().getName()+" 进入myObjectMethod。。 "); Thread.sleep(2000); System.out.println("线程 "+Thread.currentThread().getName()+" 走出myObjectMethod。。。 "); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class Service3 { public void methodA(MyObject myObject){ try { synchronized (myObject) { System.out.println("线程 " + Thread.currentThread().getName() + " 进入 methodA。。 "); Thread.sleep(2000); System.out.println("线程 " + Thread.currentThread().getName() + "走出 methodA。。。 "); } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread05; public class MyThread08_1 extends Thread{ private MyObject myObject; private Service3 service3; public MyThread08_1(MyObject myObject, Service3 service3) { this.myObject = myObject; this.service3 = service3; } @Override public void run() { super.run(); myObject.myObjectMethod(); } }
package com.it.po.thread05; public class MyThread08_2 extends Thread{ private MyObject myObject; private Service3 service3; public MyThread08_2(MyObject myObject, Service3 service3) { this.myObject = myObject; this.service3 = service3; } @Override public void run() { super.run(); service3.methodA(myObject); } }
package com.it.po.thread05; public class Run08 { public static void main(String[] args){ try { MyObject myObject = new MyObject(); Service3 service3 = new Service3(); MyThread08_1 my1 = new MyThread08_1(myObject,service3); MyThread08_2 my2= new MyThread08_2(myObject,service3); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程 B 进入 methodA。。
线程 B走出 methodA。。。
线程 A 进入myObjectMethod。。
线程 A 走出myObjectMethod。。。
3.当其他线程执行X对象方法里面的 synchronized(this)代码块时也呈同步效果(上面例子修改如下)
package com.it.po.thread05; public class MyObject { public void myObjectMethod() { try { synchronized (this) { System.out.println("线程 " + Thread.currentThread().getName() + " 进入myObjectMethod。。 "); Thread.sleep(2000); System.out.println("线程 " + Thread.currentThread().getName() + " 走出myObjectMethod。。。 "); } } catch (InterruptedException e) { e.printStackTrace(); } } }
线程 A 进入myObjectMethod。。
线程 A 走出myObjectMethod。。。
线程 B 进入 methodA。。
线程 B走出 methodA。。。