搜尋
首頁Javajava教程synchronized關鍵字的使用
synchronized關鍵字的使用Sep 06, 2019 pm 04:02 PM
synchronized

synchronized關鍵字是java並發程式設計中常用的同步鎖,用來鎖住方法或程式碼區塊,鎖定程式碼區塊時可以是synchronized(this){}、synchronized(Object){}、synchronized(類別class) {}。

synchronized關鍵字的使用

當鎖定的內容執行完或執行過程中拋出異常,才會自動釋放鎖定。

如果想要手動釋放鎖,需要呼叫鎖定的物件的wait()方法釋放掉鎖定並且置於等待狀態,切換到其他執行緒運行,而notify()方法只是喚醒一個呼叫了該對象wait()方法的其他線程,但不會釋放鎖,選擇順序也不由程式碼控制,由虛擬機器實作。

因此,物件的wait()、notify()、notifyAll()方法只能是配合synchronized關鍵字使用的,來完成執行緒間的調度。

其中鎖定方法等同於synchronized(this){方法的所有程式碼作為程式碼區塊},如下:

public synchronized void test() {
...
}

等同於

public void test() {
synchronized (this) {
...
}
}

上面的範例鎖住的是該類別的對象,如果鎖住的是個靜態的方法,我們知道靜態方法是屬於類別的而不屬於對象的,所以,synchronized修飾的靜態方法鎖定的是這個類別的所有對象,即就算是兩個實例對象,只要他們都是這個類的,那都會鎖住。

public synchronized static void test() {
	...
}

等同於

public static void test() {
synchronized (所在类.class) {
...
}	
}

無論是鎖定方法還是鎖定程式碼區塊,無論鎖定程式碼區塊時的參考物件是什麼,只要記住一個原則就一目了然了,那就是當參考對象相同時,同步鎖才起作用,否則鎖不會互斥,可以並發執行。

synchronized(this)表示當前類別的物件實例相同時鎖起作用,synchronized(Object)表示該Object物件相同時鎖起作用,synchronized(類別class)表示當都是該class類別時鎖起作用。

舉一個簡單的例子:

public class TestController {
 
    public class Task implements Runnable{
		private String str;
		
		Task(String str){
			this.str=str;
		}
		
		@Override
		public void run() {
			synchronized (str) {
				try {
					Thread.sleep(3000l);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(str);
			}
		}
		
	}
	
	public static void main(String[] args) throws InterruptedException {
		TestController testController = new TestController();
		Thread thread1 = new Thread(testController.new Task("1"));
		Thread thread2 = new Thread(testController.new Task("1"));
		thread1.start();
		thread2.start();
	}
}

上述程式碼,參考物件str都是"1",在java中,String字串如果透過this.str="1"這樣的方式賦值就等於str=String.valueOf("1"),如果字串"1"之前已經初始化過,那就會直接拿之前的,所以是同一個物件。根據上面介紹的原則,那鎖就會起作用,所以結果是3秒之後輸出1,再過3秒再輸出1。

如果把thread2改成

Thread thread2 = new Thread(testController.new Task("2"));

這時參考對像一個是"1",另一個是"2",不是同一個對象,所以鎖不會互斥,就不會起作用,所以結果是3秒後幾乎同時輸出1和2。

以上都是多個執行緒同時呼叫同一個方法,如果呼叫不同的方法呢?

public class Test{
	
	public synchronized void m1(){
		System.out.println("m1 running...");
		try {
			Thread.sleep(3000l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("m1 end");
	}
	
	public synchronized void m2(){
		System.out.println("m2 running...");
		System.out.println("m2 end");
	}
	
	public static void main(String[] args) {
		Test test = new Test();
		new Thread(new Runnable() {
			@Override
			public void run() {
				test.m1();
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				test.m2();
			}
		}).start();
	}
	
}

上面程式碼的輸出結果是:

m1 running...
//过3秒
m1 end
m2 running...
m2 end

上面就說過synchronized修飾在方法上等同於synchronized(this){方法的所有程式碼作為程式碼區塊},而this就代表是對象,也就是說,第一個Thread得到的是test物件的鎖,因為物件都是同一個test,所以第二個Thread無法取得到鎖,而被阻塞。

把上面的範例改造成如下:

private String str = "1";
	
public void m1(){
	synchronized(str){
		System.out.println("m1 running...");
		try {
			Thread.sleep(3000l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("m1 end");
	}
}
 
public void m2(){
	synchronized(str){
		System.out.println("m2 running...");
		System.out.println("m2 end");
	}
}

第一個Thread呼叫m1()時取得到的是物件str的鎖,第二個Thread呼叫m2()時也需要取得對象str的鎖,而且因為是同一個Test對象,所以兩個str也是同一個對象,所以第二個Thread會因為取得不到鎖而被阻塞,輸出結果和之前的例子一樣。

如果再把上面的例子改造成如下:

public class M1 {
	
	public void m(String str){
		synchronized (str) {
			System.out.println("m1 runing");
			try {
				Thread.sleep(3000l);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("m1 end");
		}
	}
 
}
public class M2 {
	
	public void m(String str){
		synchronized (str) {
			System.out.println("m2 runing");
			System.out.println("m2 end");
		}
	}
 
}
public class Test {
 
	public static void main(String[] args) {
		String str = "1";
		new Thread(new Runnable() {
			@Override
			public void run() {
				new M1().m(str);
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				new M2().m(str);
			}
		}).start();
	}
	
}

這次呼叫的方法在兩個類別裡面,但是結果和之前的兩個例子是一樣的,因為鎖住的都是傳進來的str對象,同一個對像只有一把鎖,第一個Thread拿了,第二個Thread就只能等待。

總結:

A. 無論synchronized關鍵字加在方法上還是物件上,如果它作用的物件是非靜態的,則它取得的鎖定是對象;如果synchronized作用的物件是一個靜態方法或一個類,則它所取得的鎖是對類,該類所有的物件同一把鎖。 

B. 每個物件只有一個鎖定(lock)與之相關聯,誰拿到這個鎖誰就可以執行它所控制的那段程式碼。 

C. 實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制

以上是synchronized關鍵字的使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:CSDN。如有侵權,請聯絡admin@php.cn刪除
Java中Synchronized的原理和使用场景以及Callable接口的使用方法及区别分析Java中Synchronized的原理和使用场景以及Callable接口的使用方法及区别分析Apr 21, 2023 am 08:04 AM

一、基本特点1.开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁.2.开始是轻量级锁实现,如果锁被持有的时间较长,就转换成重量级锁.3.实现轻量级锁的时候大概率用到的自旋锁策略4.是一种不公平锁5.是一种可重入锁6.不是读写锁二、加锁工作过程JVM将synchronized锁分为无锁、偏向锁、轻量级锁、重量级锁状态。会根据情况,进行依次升级。偏向锁假设男主是一个锁,女主是一个线程.如果只有这一个线程来使用这个锁,那么男主女主即使不领证结婚(避免了高成本操作),也可以一直幸福的生活下去.但是女配出现

Java关键字synchronized原理与锁的状态实例分析Java关键字synchronized原理与锁的状态实例分析May 11, 2023 pm 03:25 PM

一、Java中锁的概念自旋锁:是指当一个线程获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能被成功获取,直到获取到锁才会退出循环。乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,重试修改。悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。独享锁(写):给资源加上写锁,线程可以修改资源,其它线程不能再加锁(单写)。共享锁(读):给资源加上读锁后只能读不能修改,其它线程也只能加读锁,不能加写锁(多度)。看成S

Java中如何利用synchronized实现同步机制?Java中如何利用synchronized实现同步机制?Apr 22, 2023 pm 02:46 PM

Java的synchronized使用方法总结1.把synchronized当作函数修饰符时,示例代码如下:Publicsynchronizedvoidmethod(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了synchronized关键字的方法。上边的示例代码等

Java中的三种同步方式和它们的使用方法是什么?Java中的三种同步方式和它们的使用方法是什么?Apr 27, 2023 am 09:34 AM

1、说明synchronized算是我们最常用的同步方式,主要有三种使用方式。2、实例//普通类方法同步synchronizedpublidvoidinvoke(){}//类静态方法同步synchronizedpublicstaticvoidinvoke(){}//代码块同步synchronized(object){}这三种方式的不同之处在于同步的对象不同,普通类synchronized同步的是对象本身,静态方法同步的是类Class本身,代码块同步的是我们在括号内部填写的对象。Java有哪些集合

Java Synchronized锁升级原理及过程是什么Java Synchronized锁升级原理及过程是什么Apr 19, 2023 pm 10:22 PM

工具准备在正式谈synchronized的原理之前我们先谈一下自旋锁,因为在synchronized的优化当中自旋锁发挥了很大的作用。而需要了解自旋锁,我们首先需要了解什么是原子性。所谓原子性简单说来就是一个一个操作要么不做要么全做,全做的意思就是在操作的过程当中不能够被中断,比如说对变量data进行加一操作,有以下三个步骤:将data从内存加载到寄存器。将data这个值加一。将得到的结果写回内存。原子性就表示一个线程在进行加一操作的时候,不能够被其他线程中断,只有这个线程执行完这三个过程的时候

Java Synchronized是什么Java Synchronized是什么May 14, 2023 am 08:28 AM

Synchronized是什么各位Java读者,对于synchronized关键字并不陌生,在各种中间件源码或者JDK源码中都能看到,对于不熟悉synchronized的读者只知道在多线程中需要使用到synchronized关键字,知道synchronized能够保证线程安全。称之为:互斥锁(同时只能一个线程执行,其他的线程将会等待)又称之为:悲观锁(同时只能一个线程执行,其他的线程将会等待)JVM虚拟机帮你实现,开发者只需要使用synchronized关键字即可。使用时需要用一个对象当锁的互斥

Java中为什么需要提供Lock,而不仅仅使用synchronized关键字?Java中为什么需要提供Lock,而不仅仅使用synchronized关键字?Apr 20, 2023 pm 05:01 PM

摘要:在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块。既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是不是重复造轮子,多此一举呢?今天,我们就一起来探讨下这个问题。在Java中提供了synchronized关键字来保证只有一个线程能够访问同步代码块。既然已经提供了synchronized关键字,那为何在Java的SDK包中,还会提供Lock接口呢?这是不是重复造轮子,多此一举呢?今天,我们就一起来探讨下

Java中有多少个非访问修饰符?Java中有多少个非访问修饰符?Aug 30, 2023 pm 06:01 PM

Java提供了一些其他修饰符来提供除可见性之外的功能。这些修饰符称为非访问修饰符静态声明为静态的成员对于类的所有实例都是通用的。静态成员是存储在类内存中的类级别成员。Final此修饰符用于限制对变量、方法或类的进一步修改。声明为final的变量的值一旦获得值就不能修改。Final方法不能在子类中重写,也不能创建Final类的子类。抽象此修饰符可以与类或方法一起使用。您不能将此修饰符应用于变量和构造函数。声明为抽象的方法必须在子类中进行修改。您无法实例化声明为抽象的类。同步此修饰符用于控制多个线程

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具