Java中Synchronized的用法详解编程语言

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; 
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

 

【Demo1】:synchronized的用法

 

/** 
 * 同步线程 
 */ 
class SyncThread implements Runnable { 
   private static int count; 
 
   public SyncThread() { 
      count = 0; 
   } 
 
   public  void run() { 
      synchronized(this) { 
         for (int i = 0; i < 5; i++) { 
            try { 
               System.out.println(Thread.currentThread().getName() + ":" + (count++)); 
               Thread.sleep(100); 
            } catch (InterruptedException e) { 
               e.printStackTrace(); 
            } 
         } 
      } 
   } 
 
   public int getCount() { 
      return count; 
   } 
}

SyncThread的调用:

SyncThread syncThread = new SyncThread(); 
Thread thread1 = new Thread(syncThread, "SyncThread1"); 
Thread thread2 = new Thread(syncThread, "SyncThread2"); 
thread1.start(); 
thread2.start();

结果如下:

SyncThread1:0  
SyncThread1:1  
SyncThread1:2  
SyncThread1:3  
SyncThread1:4  
SyncThread2:5  
SyncThread2:6  
SyncThread2:7  
SyncThread2:8  
SyncThread2:9*

当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。 
我们再把SyncThread的调用稍微改一下:

Thread thread1 = new Thread(new SyncThread(), "SyncThread1"); 
Thread thread2 = new Thread(new SyncThread(), "SyncThread2"); 
thread1.start(); 
thread2.start();

结果如下:

SyncThread1:0  
SyncThread2:1  
SyncThread1:2  
SyncThread2:3  
SyncThread1:4  
SyncThread2:5  
SyncThread2:6  
SyncThread1:7  
SyncThread1:8  
SyncThread2:9

不是说一个线程执行synchronized代码块时其它的线程受阻塞吗?为什么上面的例子中thread1和thread2同时在执行。这是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联,而上面的代码等同于下面这段代码:

SyncThread syncThread1 = new SyncThread(); 
SyncThread syncThread2 = new SyncThread(); 
Thread thread1 = new Thread(syncThread1, "SyncThread1"); 
Thread thread2 = new Thread(syncThread2, "SyncThread2"); 
thread1.start(); 
thread2.start();

这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。

 2.当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。 
【Demo2】:多个线程访问synchronized和非synchronized代码块

class Counter implements Runnable{ 
   private int count; 
 
   public Counter() { 
      count = 0; 
   } 
 
   public void countAdd() { 
      synchronized(this) { 
         for (int i = 0; i < 5; i ++) { 
            try { 
               System.out.println(Thread.currentThread().getName() + ":" + (count++)); 
               Thread.sleep(100); 
            } catch (InterruptedException e) { 
               e.printStackTrace(); 
            } 
         } 
      } 
   } 
 
   //非synchronized代码块,未对count进行读写操作,所以可以不用synchronized 
   public void printCount() { 
      for (int i = 0; i < 5; i ++) { 
         try { 
            System.out.println(Thread.currentThread().getName() + " count:" + count); 
            Thread.sleep(100); 
         } catch (InterruptedException e) { 
            e.printStackTrace(); 
         } 
      } 
   } 
 
   public void run() { 
      String threadName = Thread.currentThread().getName(); 
      if (threadName.equals("A")) { 
         countAdd(); 
      } else if (threadName.equals("B")) { 
         printCount(); 
      } 
   } 
}

调用代码:

Counter counter = new Counter(); 
Thread thread1 = new Thread(counter, "A"); 
Thread thread2 = new Thread(counter, "B"); 
thread1.start(); 
thread2.start();

结果如下:

A:0  
B count:1  
A:1  
B count:2  
A:2  
B count:3  
A:3  
B count:4  
A:4  
B count:5

上面代码中countAdd是一个synchronized的,printCount是非synchronized的。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。


  1. 指定要给某个对象加锁

【Demo3】:指定要给某个对象加锁

/** 
 * 银行账户类 
 */ 
class Account { 
   String name; 
   float amount; 
 
   public Account(String name, float amount) { 
      this.name = name; 
      this.amount = amount; 
   } 
   //存钱 
   public  void deposit(float amt) { 
      amount += amt; 
      try { 
         Thread.sleep(100); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 
   } 
   //取钱 
   public  void withdraw(float amt) { 
      amount -= amt; 
      try { 
         Thread.sleep(100); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 
   } 
 
   public float getBalance() { 
      return amount; 
   } 
} 
 
/** 
 * 账户操作类 
 */ 
class AccountOperator implements Runnable{ 
   private Account account; 
   public AccountOperator(Account account) { 
      this.account = account; 
   } 
 
   public void run() { 
      synchronized (account) { 
         account.deposit(500); 
         account.withdraw(500); 
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance()); 
      } 
   } 
}

调用代码:

Account account = new Account("zhang san", 10000.0f); 
AccountOperator accountOperator = new AccountOperator(account); 
 
final int THREAD_NUM = 5; 
Thread threads[] = new Thread[THREAD_NUM]; 
for (int i = 0; i < THREAD_NUM; i ++) { 
   threads[i] = new Thread(accountOperator, "Thread" + i); 
   threads[i].start(); 
}

结果如下:

Thread3:10000.0  
Thread2:10000.0  
Thread1:10000.0  
Thread4:10000.0  
Thread0:10000.0

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11472.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论