在JVM中具備String常量池緩存的功能。java
public class Service { public static void print(String str){ try { synchronized (str) { while (true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(500); } } } catch (Exception e) { e.printStackTrace(); } } }
public class ThreadA extends Thread { @Override public void run() { Service.print("AA"); } } public class ThreadB extends Thread { @Override public void run() { Service.print("AA"); } }
public class Test { public static void main(String[] args) { ThreadA a = new ThreadA(); a.setName("A"); ThreadB b = new ThreadB(); b.setName("B"); a.start(); b.start(); } }
控制檯打印結果以下:編程
... A A A A A A ...
出現這種狀況就是由於Sting的兩個值都是AA,兩個線程持有相同的鎖,因此形成線程B不能執行。所以在大多數狀況下,同步synchronized代碼塊都不實用String做爲鎖對象,而改用其餘,好比new Object()實例化一個Object對象,但他並不放入緩存中。緩存
public class Service { synchronized public void methodA(){ System.out.println("methodA begin..."); boolean condition = true; while (condition) { } System.out.println("methodA end..."); } synchronized public void methodB(){ System.out.println("methodB begin..."); System.out.println("methodB end..."); } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { service.methodA(); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { service.methodB(); } }
public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); b.start(); } }
控制檯打印結果以下:安全
methodA begin...
線程A處於死循環狀態,線程B永遠沒法拿到Service對象鎖而一直得不到運行。服務器
對Service對象作以下修改:多線程
public class Service { Object object1 = new Object(); Object object2 = new Object(); public void methodA() { synchronized (object1) { System.out.println("methodA begin..."); boolean condition = true; while (condition) { } System.out.println("methodA end..."); } } public void methodB() { synchronized (object2) { System.out.println("methodB begin..."); System.out.println("methodB end..."); } } }
此時控制檯打印結果以下:app
methodA begin... methodB begin... methodB end...
methodA()和methodB()對不一樣的對象加鎖,因此線程A持有的鎖不會對線程B形成影響。異步
Java線程死鎖是一個經典的多線程問題,由於不一樣的線程都在等待根本不可能被釋放的鎖,從而致使全部的任務都沒法繼續完成。ide
public class DeadThread implements Runnable { public String username; public Object lock1 = new Object(); public Object lock2 = new Object(); public void setFlag(String username) { this.username = username; } @Override public void run() { if (username.equals("a")) { synchronized (lock1) { try { System.out.println("username=" + username); Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("按lock1->lock2代碼順序執行了"); } } } if (username.equals("b")) { synchronized (lock2) { try { System.out.println("username=" + username); Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("按lock2->lock1代碼順序執行了"); } } } } public static void main(String[] args) { try { DeadThread t1 = new DeadThread(); t1.setFlag("a"); Thread thread1 = new Thread(t1); thread1.start(); Thread.sleep(200); t1.setFlag("b"); Thread thread2 = new Thread(t1); thread2.start(); } catch (Exception e) { e.printStackTrace(); } } }
控制檯打印結果以下:性能
username=a username=b
此時程序不結束,處於死鎖狀態。
能夠使用jps命令查看當前線程的id,而後使用jstack -l id來檢查是否存在死鎖。
//內置類 public class PublicClass { private String username; private String password; class PrivateClass{ private String age; private String address; public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public void printPublicProperty() { System.out.println(username + "-" + password); } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("admin"); publicClass.setPassword("123456"); System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword()); PrivateClass privateClass = publicClass.new PrivateClass(); privateClass.setAddress("shanghai"); privateClass.setAge("25"); System.out.println(privateClass.getAddress() + "-" + privateClass.getAge()); privateClass.printPublicProperty(); } }
控制檯打印結果以下:
admin-123456 shanghai-25 admin-123456
//靜態內置類 public class PublicClass { static String username; static String password; static class PrivateClass{ private String age; private String address; public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public void printPublicProperty() { System.out.println(username + "-" + password); } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("admin"); publicClass.setPassword("123456"); System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword()); PrivateClass privateClass = new PrivateClass(); privateClass.setAddress("shanghai"); privateClass.setAge("25"); System.out.println(privateClass.getAddress() + "-" + privateClass.getAge()); privateClass.printPublicProperty(); } }
控制檯打印結果同上。
public class OutClass { static class Inner{ public void method1() { synchronized ("其餘的鎖") { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "i=" + i); try { Thread.sleep(100); } catch (Exception e) { } } } } public synchronized void method2() { for (int i = 11; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "i=" + i); try { Thread.sleep(100); } catch (Exception e) { } } } } public static void main(String[] args) { final Inner inner = new Inner(); Thread t1 = new Thread(new Runnable() { @Override public void run() { inner.method1(); } }, "A"); Thread t2 = new Thread(new Runnable() { @Override public void run() { inner.method2(); } }, "B"); t1.start(); t2.start(); } }
控制檯打印結果以下:
Ai=0 Bi=11 Bi=12 Ai=1 Bi=13 Ai=2 Ai=3 Bi=14 Ai=4 Bi=15 Bi=16 Ai=5 Ai=6 Bi=17 Ai=7 Bi=18 Bi=19 Ai=8 Ai=9
因爲持有不一樣的對象監視器,因此打印結果是亂序的。
public class OutClass { static class InnerClass1{ public void method1(InnerClass2 class2) { String threadName = Thread.currentThread().getName(); synchronized (class2) { System.out.println(threadName + "進入InnerClass1的method1方法"); for (int i = 0; i < 5; i++) { System.out.println("i=" + i); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "離開InnerClass1的method1方法"); } } public synchronized void method2() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "進入InnerClass1的method2方法"); for (int j = 0; j < 5; j++) { System.out.println("j=" + j); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "離開InnerClass1的method2方法"); } } static class InnerClass2{ public synchronized void method1() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "進入InnerClass2的method1方法"); for (int k = 0; k < 5; k++) { System.out.println("k=" + k); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "離開InnerClass2的method1方法"); } } public static void main(String[] args) { final InnerClass1 class1 = new InnerClass1(); final InnerClass2 class2 = new InnerClass2(); Thread t1 = new Thread(new Runnable() { @Override public void run() { class1.method1(class2); } }, "T1"); Thread t2 = new Thread(new Runnable() { public void run() { class1.method2(); } }, "T2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { class2.method1(); } }, "T3"); t1.start(); t2.start(); t3.start(); } }
控制檯打印結果以下:
T2進入InnerClass1的method2方法 T1進入InnerClass1的method1方法 j=0 i=0 j=1 i=1 i=2 j=2 j=3 i=3 i=4 j=4 T2離開InnerClass1的method2方法 T1離開InnerClass1的method1方法 T3進入InnerClass2的method1方法 k=0 k=1 k=2 k=3 k=4 T3離開InnerClass2的method1方法
同步代碼塊synchronized (class2)對class2上鎖後,其餘線程只能以同步方式調用class2中的靜態同步方法。
public class MyService { private String lock = "123"; public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock = "456"; Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (Exception e) { e.printStackTrace(); } } } public class ThreadA extends Thread { private MyService service; public ThreadA(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } } public class ThreadB extends Thread { private MyService service; public ThreadB(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } } public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); Thread.sleep(100); b.start(); } }
k控制檯打印結果以下:
A begin 1465980925627 B begin 1465980925727 A end 1465980927627 B end 1465980927727
從打印結果看,A線程和B線程是以異步方式執行的,可見A線程與B線程持有的鎖不一樣。
將以上main方法中的代碼作以下修改:
public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); //Thread.sleep(100); b.start(); } }
此時打印結果以下:
A begin 1465981162126 A end 1465981164127 B begin 1465981164128 B end 1465981166128
可見此時A線程和B線程是以同步方式執行的,A線程和B線程共同爭搶的鎖是「123」。
PS:只要對象不變,即便對象的屬性改變,運行的結果仍是同步的。將以上代碼作以下修改:
public class MyService { private StringBuilder lock = new StringBuilder("123"); public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock.append("456"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (Exception e) { e.printStackTrace(); } } } public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); Thread.sleep(100); b.start(); } }
此時控制檯打印結果以下:
A begin 1465981411980 A end 1465981413980 B begin 1465981413980 B end 1465981415981
可見線程A和線程B是以同步方式執行的。
死循環例子
public class PrintString { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; } public void printStringMethod() { try { while (isContinuePrint) { System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PrintString printString = new PrintString(); printString.printStringMethod(); System.out.println("中止線程..."); printString.setContinuePrint(false); } }
控制檯打印結果以下:
printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main ......
main線程在printString.printStringMethod()中陷入了死循環,後面的printString.setContinuePrint(false)得不到執行。
修改上面的代碼
public class PrintString implements Runnable { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; } public void printStringMethod() { try { while (isContinuePrint) { System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { printStringMethod(); } public static void main(String[] args) throws InterruptedException { PrintString printString = new PrintString(); new Thread(printString).start(); Thread.sleep(5000); System.out.println("中止線程..."); printString.setContinuePrint(false); } }
此時控制檯打印結果以下:
printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 中止線程...
此時main線程設置isContinuePrint=false,能夠使另外一個線程中止執行。
++注:《Java多線程編程核心技術》P120講將上面的代碼運行在-server服務器模式中的64bit的JVM上時,會出現死循環。實際測試並未出現死循環,暫未弄清緣由。++
package com.umgsai.thread22; public class RunThread extends Thread { volatile private boolean isRunning = true; public boolean isRunning() { return isRunning; } public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() { System.out.println("進入run方法..."); while (isRunning) { System.out.println("running...."); try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } } System.out.println("線程被中止..."); } public static void main(String[] args) { try { RunThread runThread = new RunThread(); runThread.start(); Thread.sleep(1000); runThread.setRunning(false); System.out.println("已將isRunning設置爲false"); } catch (Exception e) { e.printStackTrace(); } } }
控制檯打印結果以下:
進入run方法... running.... running.... 已將isRunning設置爲false 線程被中止...
==使用volatile關鍵字強制從公共內存中讀取變量==。
使用volatile關鍵字增長了實例變量在多個線程之間的可見性,可是volatile關鍵字不支持原子性。
synchronized和volatile的比較
public class VolatileTest extends Thread { volatile public static int count; private static void addCount(){ for (int i = 0; i < 100; i++) { count++; } System.out.println("count=" + count); } @Override public void run() { addCount(); } public static void main(String[] args) { VolatileTest[] volatileTests = new VolatileTest[100]; for (int i = 0; i < 100; i++) { volatileTests[i] = new VolatileTest(); } for (int i = 0; i < 100; i++) { volatileTests[i].start(); } } }
控制檯打印結果以下:
...... count=5332 count=5232 count=5132 count=5032 count=4932 count=4854 count=4732 count=4732
將以上代碼中的addCount方法加上synchronized關鍵字
public class VolatileTest extends Thread { volatile public static int count; //必定要加static關鍵字,這樣synchronized與static鎖的內容就是VolatileTest類了,也就達到同步效果了。 synchronized private static void addCount(){ for (int i = 0; i < 100; i++) { count++; } System.out.println("count=" + count); } @Override public void run() { addCount(); } public static void main(String[] args) { VolatileTest[] volatileTests = new VolatileTest[100]; for (int i = 0; i < 100; i++) { volatileTests[i] = new VolatileTest(); } for (int i = 0; i < 100; i++) { volatileTests[i].start(); } } }
此時控制檯打印結果以下:
...... count=9300 count=9400 count=9500 count=9600 count=9700 count=9800 count=9900 count=10000
i++操做除了使用synchronized關鍵字同步外,還能夠使用AtomicInteger原子類實現。
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest extends Thread { private AtomicInteger count = new AtomicInteger(0); @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(count.incrementAndGet()); } } public static void main(String[] args) { AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest(); Thread t1 = new Thread(atomicIntegerTest); Thread t2 = new Thread(atomicIntegerTest); Thread t3 = new Thread(atomicIntegerTest); Thread t4 = new Thread(atomicIntegerTest); Thread t5 = new Thread(atomicIntegerTest); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
控制檯打印結果以下:
...... 4992 4993 4994 4995 4996 4997 4998 4999 5000
5個線程成功累加到5000。
public class MyService { public static AtomicLong atomicLong = new AtomicLong(); public void addNum() { System.out.println(Thread.currentThread().getName() + " 加了100以後是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } } public class MyThread extends Thread { private MyService myService; public MyThread(MyService myService) { this.myService = myService; } @Override public void run() { myService.addNum(); } } public class Run { public static void main(String[] args) { try { MyService myService = new MyService(); MyThread[] array = new MyThread[100]; for (int i = 0; i < array.length; i++) { array[i] = new MyThread(myService); } for (int i = 0; i < array.length; i++) { array[i].start();; } Thread.sleep(1000); System.out.println(myService.atomicLong.get()); } catch (Exception e) { e.printStackTrace(); } } }
控制檯打印結果以下:
...... Thread-89 加了100以後是:8987 Thread-96 加了100以後是:9493 Thread-80 加了100以後是:9594 Thread-94 加了100以後是:9695 Thread-97 加了100以後是:9796 Thread-95 加了100以後是:9896 Thread-98 加了100以後是:9998 Thread-99 加了100以後是:10098 10100
累加的結果是正確的,可是打印順序的錯的,這是由於雖然addAndGet方法是原子的,可是方法和方法之間的調用卻不是原子的。
對以上代碼作以下修改:
public class MyService { public static AtomicLong atomicLong = new AtomicLong(); synchronized public void addNum() { System.out.println(Thread.currentThread().getName() + " 加了100以後是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } }
此時控制檯打印結果以下:
...... Thread-86 加了100以後是:9392 Thread-87 加了100以後是:9493 Thread-88 加了100以後是:9594 Thread-92 加了100以後是:9695 Thread-93 加了100以後是:9796 Thread-94 加了100以後是:9897 Thread-95 加了100以後是:9998 Thread-98 加了100以後是:10099 10100
此時線程以同步方式執行addNum方法,每次先加100再加1.
關鍵字synchronized能夠使多個線程訪問同一個資源具備同步性,並且還能夠將線程工做內存中的私有變量與公共內存中的變量進行同步。
public class Service { private boolean isContinueRun = true; public void runMethod() { while (isContinueRun) { } System.out.println("stop..."); } public void stopMethod() { isContinueRun = false; } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.runMethod(); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { service.stopMethod(); } }
public class Run { public static void main(String[] args) { try { Service service = new Service(); ThreadA a = new ThreadA(service); a.start(); Thread.sleep(1000); ThreadB b = new ThreadB(service); b.start(); System.out.println("已經發起中止命令了"); } catch (Exception e) { e.printStackTrace(); } } }
運行main,控制檯打印結果以下:
已經發起中止命令了
出現死循環。
對Service類作以下修改:
public class Service { private boolean isContinueRun = true; public void runMethod() { String anyString = new String(); while (isContinueRun) { synchronized (anyString) { } } System.out.println("stop..."); } public void stopMethod() { isContinueRun = false; } }
此時控制檯打印結果以下:
已經發起中止命令了 stop...
關鍵字synchronized能夠保證在同一時刻,只有一個線程能夠執行某一個方法或某一個代碼塊。它包含兩個特徵:互斥性和可見性。同步synchronized不只能夠解決一個線程看到對象處於不一致的狀態,還能夠保證進入同步方法或者同步代碼塊的每一個線程都看到由同一個鎖保護以前全部的修改結果。