1.synchronized鎖重入java
public class Service { synchronized public void service1(){ System.out.println("service1"); service2(); } synchronized public void service2(){ System.out.println("service2"); service3(); } synchronized public void service3(){ System.out.println("service2"); } }
//線程類 public class MyThread extends Thread{ @Override public void run() { Service service=new Service(); service.service1(); } }
//測試類
public class Run { public static void main(String[] args) { MyThread thread=new MyThread(); thread.start(); } /* output: service1 service2 service3 */ }
總結:本身能夠再次獲取本身的內部鎖.也支持在父子類繼承的環境中dom
2.出現異常,鎖自動釋放異步
public class Service { synchronized public void testMethod(){ if(Thread.currentThread().getName().equals("a")){ System.out.println("ThreadName="+Thread.currentThread().getName() +" run beginTime="+System.currentTimeMillis()); int i=1; while(i==1){ if((""+Math.random()).substring(0,8).equals("0.123456")){ System.out.println("ThreadName="+Thread.currentThread().getName() +" run exceptionTime="+System.currentTimeMillis()); Integer.parseInt("a"); } } }else{ System.out.println("Thread B runt Time="+System.currentTimeMillis()); } } }
public class Test { public static void main(String[] args) { try { Service service=new Service(); ThreadA a=new ThreadA(service); a.setName("a"); a.start(); Thread.sleep(500); ThreadB b=new ThreadB(service); b.setName("b"); b.start(); } catch (InterruptedException e) { e.printStackTrace(); } } /*output: ThreadName=a run beginTime=1523554493390 ThreadName=a run exceptionTime=1523554494218 //a報錯,鎖自動釋放 Thread B runt Time=1523554494219 Exception in thread "a" java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:580) at java.lang.Integer.parseInt(Integer.java:615) at us.codecraft.dxc.Service.testMethod(Service.java:16) at us.codecraft.dxc.ThreadA.run(ThreadA.java:36) */ }
3.同步不具備繼承性ide
//父類 public class Main { synchronized public void serviceMethod(){ try { System.out.println("int main 下一步 sleep begin threadName=" +Thread.currentThread().getName()+" time="+System.currentTimeMillis() ); Thread.sleep(5000); System.out.println("int main 下一步 sleep end threadName="+ Thread.currentThread().getName()+" time="+ System.currentTimeMillis() ); } catch (InterruptedException e) { e.printStackTrace(); } } }
//子類 public class Sub extends Main{ @Override public void serviceMethod() { try { System.out.println("int sub 下一步 sleep begin threadName=" +Thread.currentThread().getName()+" time="+System.currentTimeMillis() ); Thread.sleep(5000); System.out.println("int sub 下一步 sleep end threadName="+ Thread.currentThread().getName()+" time="+ System.currentTimeMillis() ); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }
//線程A public class ThreadA extends Thread{ private Sub sub; public ThreadA(Sub sub){ super(); this.sub=sub; } @Override public void run() { sub.serviceMethod(); } }
//線程B public class ThreadB extends Thread{ private Sub sub; public ThreadB(Sub sub){ super(); this.sub=sub; } @Override public void run() { sub.serviceMethod(); } }
public class Test { public static void main(String[] args) { Sub subRef=new Sub(); ThreadA a=new ThreadA(subRef); a.setName("A"); a.start(); ThreadB b=new ThreadB(subRef); b.setName("B"); b.start(); } /*output: int sub 下一步 sleep begin threadName=B time=1523556610729 int sub 下一步 sleep begin threadName=A time=1523556610729 int sub 下一步 sleep end threadName=A time=1523556615729 int sub 下一步 sleep end threadName=B time=1523556615729 int main 下一步 sleep begin threadName=A time=1523556615729 int main 下一步 sleep end threadName=A time=1523556620730 int main 下一步 sleep begin threadName=B time=1523556620730 int main 下一步 sleep end threadName=B time=1523556625730 */ }
4.synchronized的弊端:測試
package us.codecraft.dxc; /** * Created by Administrator on 2018/4/13. */ public class Task { private String getDate1; private String getDate2; public synchronized void doLongTimeTask(){ try { System.out.println("begin task"); Thread.sleep(3000); getDate1="長時間處理任務後從遠程返回值1 threadName="+Thread.currentThread().getName(); getDate2="長時間處理任務後從遠程返回值2 threadName="+Thread.currentThread().getName(); System.out.println(getDate1); System.out.println(getDate2); System.out.println("end task"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class CommonUtils { public static long beginTime1; public static long endTime1; public static long beginTime2; public static long endTime2; }
public class MyThread1 extends Thread{ private Task task; public MyThread1(Task task){ super(); this.task=task; } @Override public void run() { super.run(); CommonUtils.beginTime1=System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime1=System.currentTimeMillis(); } }
public class MyThread2 extends Thread{ private Task task; public MyThread2(Task task){ super(); this.task=task; } @Override public void run() { super.run(); CommonUtils.beginTime2=System.currentTimeMillis(); task.doLongTimeTask(); CommonUtils.endTime2=System.currentTimeMillis(); } }
public class Run { public static void main(String[] args) { Task task=new Task(); MyThread1 thread1=new MyThread1(task); thread1.start(); MyThread2 thread2=new MyThread2(task); thread2.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } long beginTime=CommonUtils.beginTime1; if(CommonUtils.beginTime2<CommonUtils.beginTime1){ beginTime=CommonUtils.beginTime2; } long endTime=CommonUtils.endTime1; if (CommonUtils.endTime2>CommonUtils.endTime1) { endTime = CommonUtils.endTime2; } System.out.println("耗時"+((endTime-beginTime)/1000)); } /*output: begin task 長時間處理任務後從遠程返回值1 threadName=Thread-0 長時間處理任務後從遠程返回值2 threadName=Thread-0 end task begin task 長時間處理任務後從遠程返回值1 threadName=Thread-1 長時間處理任務後從遠程返回值2 threadName=Thread-1 end task 耗時6*/ }
一段時間內只能有一個線程被執行,另外一個線程必須等待當前線程執行完代碼之後才能執行該代碼塊this
解決方法:使用同步代碼塊線程
public class Task { private String getDate1; private String getDate2; public void doLongTimeTask(){ try { System.out.println("begin task"); Thread.sleep(3000); synchronized (this){ getDate1="長時間處理任務後從遠程返回值1 threadName="+Thread.currentThread().getName(); getDate2="長時間處理任務後從遠程返回值2 threadName="+Thread.currentThread().getName(); } System.out.println(getDate1); System.out.println(getDate2); System.out.println("end task"); } catch (InterruptedException e) { e.printStackTrace(); } } /**output: begin task begin task 長時間處理任務後從遠程返回值1 threadName=t1 長時間處理任務後從遠程返回值2 threadName=t1 end task 長時間處理任務後從遠程返回值1 threadName=t2 長時間處理任務後從遠程返回值2 threadName=t2 end task 耗時3 / }
5.多個線程調用同一個對象中的不一樣名稱的synchronized同步方法或者synchronized(this)同步代碼塊時,調用的效果是按照順序執行,也就是同步的,阻塞的code
synchronized同步方法和synchronized(this)同步代碼塊分別有兩種做用:orm
對其餘synchronized同步方法或者synchronized(this)同步代碼塊調用呈阻塞狀態對象
同一時間只有一個線程能夠執行或者synchronized()同步方法或者synchronized(this)同步代碼中的代碼
6.將任意對象做爲對象監視器
public class Service { private String password; private String username; String anyString =new String(); public void setUsernamePass(String username,String password){ try { synchronized (anyString) { System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入同步塊"); Thread.sleep(3000); this.username = username; this.password=password; System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開同步塊"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class ThreadA extends Thread{ private Service service; public ThreadA(Service service){ super(); this.service=service; } @Override public void run() { service.setUsernamePass("a","aa"); } }
public class ThreadB extends Thread{ private Service service; public ThreadB(Service service){ super(); this.service=service; } @Override public void run() { service.setUsernamePass("b","bb"); } }
public class Run { public static void main(String[] args) throws InterruptedException { Service service=new Service(); ThreadA threadA=new ThreadA(service); threadA.setName("A"); threadA.start(); ThreadB threadB=new ThreadB(service); threadB.setName("B"); threadB.start(); } /*output: 線程名爲:A在1522229473958進入同步塊 線程名爲:A在1522229476969離開同步塊 線程名爲:B在1522229476969進入同步塊 線程名爲:B在1522229479980離開同步塊 //同步,阻塞,先由A進入,再由B進入 */ }
總結:咱們能夠看出synchronized(非this對象)若是是公共資源(調用同一個類對象,兩個線程影響同一個anyString)就是實現同步,阻塞功能.由此咱們能夠得出,若是synchronized(非this對象)鎖住的是私有變量的話,就能夠實現異步,非阻塞的功能,下面咱們來實驗一下.
public class Service { private String password; private String username; public void setUsernamePass(String username,String password){ try { String anyString =new String();//將變量設置爲局部變量 synchronized (anyString) { System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進入同步塊"); Thread.sleep(3000); this.username = username; this.password=password; System.out.println("線程名爲:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開同步塊"); } } catch (InterruptedException e) { e.printStackTrace(); } } /*output: 線程名爲:A在1522229832397進入同步塊 線程名爲:B在1522229832397進入同步塊 線程名爲:A在1522229835408離開同步塊 線程名爲:B在1522229835408離開同步塊 //兩個線程同時進入方法,非阻塞,異步 */ }
總結:大大提升了運行效率.
7.靜態同步synchronized方法與synchronized(class)代碼塊
synchronized應用在static靜態方法上,這樣是對當前的*.class文件對應的class類進行持鎖.
例子:
public class Service { synchronized public static void printA(){ try { System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"進入printA"); Thread.sleep(3000); System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"離開printA"); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public static void printB(){ System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"進入printB"); System.out.println("線程名稱爲:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"離開printB"); } }
public class ThreadA extends Thread{ @Override public void run() { Service.printA(); } }
public class ThreadB extends Thread{ @Override public void run() { Service.printB(); } }
public class Run { public static void main(String[] args) throws InterruptedException { ThreadA a =new ThreadA(); a.setName("a"); a.start(); ThreadB b=new ThreadB(); b.setName("b"); b.start(); } /*output: 線程名稱爲:a在1515601919412進入printA 線程名稱爲:a在1515601922412離開printA 線程名稱爲:b在1515601922412進入printB 線程名稱爲:b在1515601922412離開printB */ }
總結:synchronized關鍵字加到static靜態方法上是給class類上鎖,而synchronized關鍵字加到非static靜態方法上是給對象加鎖