java多線程中synchronize鎖的使用和學習,Thread多線程學習(二),synchronizethread
synchronize我的理解是為了保證程序中的原子性和一致性,即當你有兩個線程同時操作一段代碼的時候,要讓這段代碼的執行是在任何狀態下都是正確的,首先要保證synchronize的使用要對同一個對象和同一把鎖使用。
[java] view plain copy
print?
- <span >public class TraditionalThreadSynchronized {
-
-
- public static void main(String[] args) {
- TraditionalThreadSynchronized test =new TraditionalThreadSynchronized();
- test.init();//創建同一個對象
- }
-
- private void init(){
- OutPuter outPuter =new OutPuter();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(true){
- Thread.sleep(10);
- outPuter.outer("abcdefg");
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }).start();//線程1
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(true){
- Thread.sleep(10);
- outPuter.outer("123456789");
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }).start();//線程2
- }
- class OutPuter {
- public synchronized void outer(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我們想要打印的結果是abcdefg和123456789交替打印輸出,但是輸出結果卻是下圖
我們發現打印的結果是兩個String交替打印出來的,所以這個線程現在是不安全的,那麼此時我們就需要使用synchronize保持兩個線程的原子性,下面我們把上面程序中的OutPut方法加上synchronize鎖試一下
[java] view plain copy
print?
- <span > class OutPuter {
- public void outer(String name) {
- synchronized (name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我們發現加了synchronize關鍵字的鎖以後還是會出現這種線程不安全的情況,那是為什麼呢?原因就出在這個鎖name上,因為兩個線程中的name一個是abcdefg一個是123456789,兩個鎖根本不是一個鎖,所以線程也是不安全的,那麼我們再把上面的方法修改一下
[java] view plain copy
print?
- <span >class OutPuter {
- public void outer(String name) {
- synchronized (this) {//或者創建一個新的鎖把,this換成鎖也可以,此處的this指的是outer這個對象
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我們發現此時,上面的程序運行不會出現線程安全問題。
我們此時把上面的syn加在outer上面也是可以的
[java] view plain copy
print?
- class OutPuter {
- public synchronized void outer(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
這樣也是可以實現線程安全的,但是一般情況下synchronize在一段代碼中只要使用一次就夠了,因為多個synchronize可能會出現死鎖問題!
此時我們把上面的OutPuter類修改一下
[java] view plain copy
print?
- class OutPuter {
- public void outer(String name) {
- synchronized (this) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- public synchronized void outer2(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- span > }</span>
然後把上面的outPuter.outer(“123456789”)改成outPuter.outer2(“123456789”)
此時還能保證線程安全麼,也就是說output和output2會保持互斥麼?當然也是可以的,因為此時的兩個鎖代表的也是同一把鎖,都是代表當前的對象,而當前對象此時是一個對象,所以是可以實現我們想要的效果的。
那麼此時我們把上面的
然後把上面的outPuter.outer(“123456789”)改成new outPuter().outer(“123456789”)
我們會發現依然會出現線程安全問題,那是為什麼呢 ?那是因為我們每次new的OutPuter()並不是同一個對象。所以說明synchronize既要保證是同一個對象,又要保證是同一把鎖。
此時我們再加一個output3方法
[java] view plain copy
print?
- public static synchronized void outer3(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- span > }</span>
注意這個output3方法是靜態的,那麼我們需要把OutPuter類也改成靜態類
然後把上面的outPuter.outer(“123456789”)改成outPuter.outer3(“123456789”)
答案是不行的,因為此時我們使用的output3中的鎖是類鎖,而output中的鎖是當前對象鎖,根本不是一把鎖,那麼此時我們要是想讓output和output3同步需要怎麼做呢?我們把output中的this改成OutPuter.class就可以實現同步了。此時我們兩個方法的鎖就都是類鎖了,即為同一把鎖。
線程還在學習中,學習是痛苦的,但是我希望能真正的堅持做一件事情!