Có ai có thể cho tôi biết lợi thế của phương thức được đồng bộ hóa so với khối được đồng bộ hóa với một ví dụ không?
Có ai có thể cho tôi biết lợi thế của phương thức được đồng bộ hóa so với khối được đồng bộ hóa với một ví dụ không?
Câu trả lời:
Ai đó có thể cho tôi biết lợi thế của phương thức được đồng bộ hóa so với khối được đồng bộ hóa với một ví dụ không? Cảm ơn.
Không có một lợi thế rõ ràng của việc sử dụng phương pháp đồng bộ hóa trên khối.
Có lẽ điều duy nhất (nhưng tôi sẽ không gọi đó là lợi thế) là bạn không cần đưa vào tham chiếu đối tượng this
.
Phương pháp:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Khối:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
Xem? Không có lợi thế nào cả.
Khối làm có ưu điểm so với các phương pháp tuy nhiên, hầu hết là ở sự linh hoạt bởi vì bạn có thể sử dụng đối tượng khác như khóa trong khi đồng bộ phương pháp này sẽ khóa toàn bộ đối tượng.
Đối chiếu:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
so với
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Ngoài ra, nếu phương thức phát triển, bạn vẫn có thể tách riêng phần được đồng bộ hóa:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
khối được thực hiện sử dụng hai hướng dẫn, monitorenter
và monitorexit
, cộng với một bộ xử lý ngoại lệ mà đảm bảo rằng monitorexit
được gọi là ngay cả trong những trường hợp đặc biệt. Đó là tất cả được lưu khi sử dụng một synchronized
phương pháp.
Sự khác biệt thực sự duy nhất là một khối được đồng bộ hóa có thể chọn đối tượng mà nó đồng bộ hóa trên đó. Một phương thức được đồng bộ hóa chỉ có thể sử dụng 'this'
(hoặc thể hiện Class tương ứng cho một phương thức lớp được đồng bộ hóa). Ví dụ: đây là tương đương về mặt ngữ nghĩa:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
Cái sau linh hoạt hơn vì nó có thể cạnh tranh cho khóa liên quan của bất kỳ đối tượng nào , thường là biến thành viên. Nó cũng chi tiết hơn vì bạn có thể thực thi mã đồng thời trước và sau khối nhưng vẫn nằm trong phương thức. Tất nhiên, bạn có thể dễ dàng sử dụng một phương thức được đồng bộ hóa bằng cách tái cấu trúc mã đồng thời thành các phương thức không đồng bộ hóa riêng biệt. Sử dụng bất cứ điều gì làm cho mã dễ hiểu hơn.
Ưu điểm:
Nhược điểm:
Ưu điểm:
Nhược điểm:
Cá nhân tôi thích sử dụng các phương thức được đồng bộ hóa với các lớp chỉ tập trung vào điều cần đồng bộ hóa. Lớp như vậy nên càng nhỏ càng tốt và vì vậy nó sẽ dễ dàng để xem lại sự đồng bộ hóa. Những người khác không cần quan tâm đến việc đồng bộ hóa.
Sự khác biệt chính là nếu bạn sử dụng một khối được đồng bộ hóa, bạn có thể khóa trên một đối tượng khác với điều này cho phép linh hoạt hơn nhiều.
Giả sử bạn có một hàng đợi tin nhắn và nhiều nhà sản xuất và người tiêu dùng tin nhắn. Chúng tôi không muốn các nhà sản xuất can thiệp lẫn nhau, nhưng người tiêu dùng sẽ có thể truy xuất tin nhắn mà không phải chờ nhà sản xuất. Vì vậy, chúng tôi chỉ cần tạo ra một đối tượng
Object writeLock = new Object();
Và kể từ bây giờ mỗi khi nhà sản xuất muốn thêm một thông điệp mới, chúng tôi chỉ khóa vào đó:
synchronized(writeLock){
// do something
}
Vì vậy, người tiêu dùng vẫn có thể đọc, và nhà sản xuất sẽ bị khóa.
Phương pháp đồng bộ hóa
Phương pháp đồng bộ có hai hiệu ứng.
Đầu tiên, khi một luồng đang thực thi một phương thức được đồng bộ hóa cho một đối tượng, tất cả các luồng khác gọi các phương thức được đồng bộ hóa cho cùng một khối đối tượng (tạm dừng thực thi) cho đến khi luồng đầu tiên được thực hiện với đối tượng.
Thứ hai, khi một phương thức được đồng bộ hóa thoát ra, nó sẽ tự động thiết lập mối quan hệ xảy ra trước khi có bất kỳ lời gọi tiếp theo nào của một phương thức được đồng bộ hóa cho cùng một đối tượng. Điều này đảm bảo rằng những thay đổi về trạng thái của đối tượng được hiển thị cho tất cả các luồng.
Lưu ý rằng các hàm tạo không thể được đồng bộ hóa - sử dụng từ khóa được đồng bộ hóa với hàm tạo là một lỗi cú pháp. Đồng bộ hóa các hàm tạo không có ý nghĩa gì, bởi vì chỉ luồng tạo ra một đối tượng mới có quyền truy cập vào nó trong khi nó đang được xây dựng.
Báo cáo đồng bộ hóa
Không giống như các phương thức được đồng bộ hóa, các câu lệnh được đồng bộ hóa phải chỉ định đối tượng cung cấp khóa nội tại: Thông thường tôi sử dụng điều này để đồng bộ hóa quyền truy cập vào danh sách hoặc bản đồ nhưng tôi không muốn chặn quyền truy cập vào tất cả các phương thức của đối tượng.
Q: Khóa nội bộ và Đồng bộ hóa đồng bộ hóa được xây dựng xung quanh một thực thể bên trong được gọi là khóa nội tại hoặc khóa màn hình. (Đặc tả API thường đề cập đến thực thể này đơn giản là "màn hình".) Khóa nội tại đóng vai trò trong cả hai khía cạnh của đồng bộ hóa: thực thi quyền truy cập độc quyền vào trạng thái của đối tượng và thiết lập các mối quan hệ xảy ra trước khi cần thiết.
Mỗi đối tượng có một khóa nội tại liên quan đến nó. Theo quy ước, một luồng cần truy cập độc quyền và nhất quán vào các trường của đối tượng phải có được khóa nội tại của đối tượng trước khi truy cập vào chúng, và sau đó giải phóng khóa nội tại khi thực hiện với chúng. Một chủ đề được cho là sở hữu khóa nội tại giữa thời gian nó có được khóa và phát hành khóa. Miễn là một luồng sở hữu một khóa nội tại, không có luồng nào khác có thể có được cùng một khóa. Các luồng khác sẽ chặn khi nó cố gắng để có được khóa.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Kiểm tra chéo các đầu ra khác nhau với phương thức đồng bộ, chặn và không đồng bộ hóa.
Lưu ý: các phương thức và khối được đồng bộ hóa tĩnh hoạt động trên đối tượng Class.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Khi trình biên dịch java chuyển đổi mã nguồn của bạn thành mã byte, nó xử lý các phương thức được đồng bộ hóa và các khối được đồng bộ hóa rất khác nhau.
Khi JVM thực thi một phương thức được đồng bộ hóa, luồng thực thi xác định rằng cấu trúc method_info của phương thức có cờ ACC_SYNCHRONIZED, sau đó nó tự động lấy khóa của đối tượng, gọi phương thức và giải phóng khóa. Nếu một ngoại lệ xảy ra, luồng sẽ tự động giải phóng khóa.
Mặt khác, đồng bộ hóa một khối phương thức, bỏ qua hỗ trợ tích hợp của JVM để có được khóa và xử lý ngoại lệ của đối tượng và yêu cầu chức năng phải được ghi rõ ràng bằng mã byte. Nếu bạn đọc mã byte cho một phương thức có khối được đồng bộ hóa, bạn sẽ thấy hơn một chục thao tác bổ sung để quản lý chức năng này.
Điều này hiển thị các lệnh gọi để tạo cả phương thức được đồng bộ hóa và khối được đồng bộ hóa:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
Các synchronizedMethodGet()
phương pháp tạo ra mã byte sau:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
Và đây là mã byte từ synchronizedBlockGet()
phương thức:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Một sự khác biệt đáng kể giữa phương thức và khối được đồng bộ hóa là, khối được đồng bộ hóa thường giảm phạm vi khóa. Vì phạm vi khóa tỷ lệ nghịch với hiệu suất, nên chỉ khóa phần quan trọng của mã. Một trong những ví dụ tốt nhất về việc sử dụng khối được đồng bộ hóa là khóa được kiểm tra hai lần trong mẫu Singleton trong đó thay vì khóa toàn bộ getInstance()
phương thức, chúng tôi chỉ khóa phần mã quan trọng được sử dụng để tạo cá thể Singleton. Điều này cải thiện hiệu suất mạnh mẽ vì khóa chỉ được yêu cầu một hoặc hai lần.
Trong khi sử dụng các phương thức được đồng bộ hóa, bạn sẽ cần phải cẩn thận hơn nếu bạn trộn lẫn cả hai phương thức được đồng bộ hóa tĩnh và không tĩnh.
monitorenter
và monitorexit
trước khi chạy mã.
Thông thường tôi sử dụng điều này để đồng bộ hóa quyền truy cập vào danh sách hoặc bản đồ nhưng tôi không muốn chặn quyền truy cập vào tất cả các phương thức của đối tượng.
Trong đoạn mã sau, một luồng sửa đổi danh sách sẽ không chặn chờ một luồng đang sửa đổi bản đồ. Nếu các phương thức được đồng bộ hóa trên đối tượng thì mỗi phương thức sẽ phải chờ mặc dù các sửa đổi mà chúng đang thực hiện sẽ không xung đột.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
Với các khối được đồng bộ hóa, bạn có thể có nhiều bộ đồng bộ hóa, để nhiều thứ đồng thời nhưng không xung đột có thể diễn ra cùng một lúc.
Phương pháp đồng bộ hóa có thể được kiểm tra bằng API phản chiếu. Điều này có thể hữu ích để thử nghiệm một số hợp đồng, chẳng hạn như tất cả các phương thức trong mô hình được đồng bộ hóa .
Đoạn mã sau in tất cả các phương thức được đồng bộ hóa của Hashtable:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Lưu ý quan trọng về việc sử dụng khối được đồng bộ hóa: cẩn thận những gì bạn sử dụng làm đối tượng khóa!
Đoạn mã từ user2277816 ở trên minh họa điểm này trong đó một tham chiếu đến một chuỗi ký tự được sử dụng làm đối tượng khóa. Nhận ra rằng chuỗi ký tự được tự động thực hiện trong Java và bạn sẽ bắt đầu thấy vấn đề: mọi đoạn mã được đồng bộ hóa trên "khóa" theo nghĩa đen, chia sẻ cùng một khóa! Điều này có thể dễ dàng dẫn đến bế tắc với các đoạn mã hoàn toàn không liên quan.
Nó không chỉ là các đối tượng String mà bạn cần phải cẩn thận. Các nguyên hàm đóng hộp cũng là một mối nguy hiểm, vì autoboxing và các phương thức valueOf có thể sử dụng lại các đối tượng tương tự, tùy thuộc vào giá trị.
Để biết thêm thông tin, hãy xem: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synyncize+on+objects+that+may+be+reuse
Thường sử dụng khóa trên cấp độ phương pháp là quá thô lỗ. Tại sao khóa một đoạn mã không truy cập bất kỳ tài nguyên được chia sẻ nào bằng cách khóa toàn bộ phương thức. Vì mỗi đối tượng có một khóa, bạn có thể tạo các đối tượng giả để thực hiện đồng bộ hóa cấp khối. Mức khối hiệu quả hơn vì nó không khóa toàn bộ phương thức.
Dưới đây là một số ví dụ
Cấp độ phương pháp
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Cấp khối
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Biên tập]
Để Collection
thích Vector
và Hashtable
chúng được đồng bộ hóa khi ArrayList
hoặc HashMap
không và bạn cần đặt từ khóa được đồng bộ hóa hoặc gọi phương thức được đồng bộ hóa Bộ sưu tập:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Sự khác biệt duy nhất: các khối được đồng bộ hóa cho phép khóa dạng hạt không giống như phương thức được đồng bộ hóa
Về cơ bản synchronized
khối hoặc phương thức đã được sử dụng để viết mã an toàn của luồng bằng cách tránh các lỗi không nhất quán bộ nhớ.
Câu hỏi này rất cũ và nhiều thứ đã được thay đổi trong suốt 7 năm qua. Các cấu trúc lập trình mới đã được giới thiệu để đảm bảo an toàn cho luồng.
Bạn có thể đạt được sự an toàn của luồng bằng cách sử dụng API đồng thời nâng cao thay vì các synchronied
khối. Trang tài liệu này cung cấp các cấu trúc lập trình tốt để đạt được sự an toàn của luồng.
Khóa Đối tượng hỗ trợ khóa thành ngữ đơn giản hóa nhiều ứng dụng đồng thời.
Các nhà điều hành xác định API cấp cao để khởi chạy và quản lý các luồng. Việc triển khai thực thi được cung cấp bởi java.util.conc hiện cung cấp quản lý nhóm luồng phù hợp cho các ứng dụng quy mô lớn.
Bộ sưu tập đồng thời giúp quản lý bộ sưu tập dữ liệu lớn dễ dàng hơn và có thể giảm đáng kể nhu cầu đồng bộ hóa.
Biến nguyên tử có các tính năng giảm thiểu đồng bộ hóa và giúp tránh các lỗi thống nhất bộ nhớ.
ThreadLocalRandom (trong JDK 7) cung cấp việc tạo số giả ngẫu nhiên hiệu quả từ nhiều luồng.
Thay thế tốt hơn cho đồng bộ hóa là ReentrantLock , sử dụng Lock
API
Khóa loại trừ lẫn nhau reentrant với cùng một hành vi và ngữ nghĩa cơ bản giống như khóa màn hình ngầm được truy cập bằng các phương thức và câu lệnh được đồng bộ hóa, nhưng với khả năng mở rộng.
Ví dụ với khóa:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Tham khảo java.util.concurrent và java.util.concurrent.atomic gói quá cho các cấu trúc lập trình khác.
Tham khảo câu hỏi liên quan này quá:
Nói chung, những thứ này hầu hết đều giống nhau ngoài việc rõ ràng về màn hình của đối tượng đang được sử dụng so với đối tượng ẩn này. Một nhược điểm của các phương thức được đồng bộ hóa mà tôi nghĩ đôi khi bị bỏ qua là trong việc sử dụng tham chiếu "này" để đồng bộ hóa trên bạn sẽ bỏ ngỏ khả năng các đối tượng bên ngoài khóa trên cùng một đối tượng. Đó có thể là một lỗi rất tinh tế nếu bạn gặp phải nó. Đồng bộ hóa trên Đối tượng rõ ràng nội bộ hoặc trường hiện có khác có thể tránh được sự cố này, hoàn toàn đóng gói đồng bộ hóa.
Như đã nói ở đây, khối được đồng bộ hóa có thể sử dụng biến do người dùng xác định làm đối tượng khóa, khi chức năng được đồng bộ hóa chỉ sử dụng "này". Và tất nhiên bạn có thể thao tác với các khu vực chức năng của bạn sẽ được đồng bộ hóa. Nhưng mọi người đều nói rằng không có sự khác biệt giữa chức năng được đồng bộ hóa và khối bao gồm toàn bộ chức năng sử dụng "this" làm đối tượng khóa. Điều đó không đúng, sự khác biệt nằm ở mã byte sẽ được tạo ra trong cả hai tình huống. Trong trường hợp sử dụng khối đồng bộ nên được phân bổ biến cục bộ chứa tham chiếu đến "này". Và kết quả là chúng ta sẽ có kích thước lớn hơn một chút cho chức năng (không liên quan nếu bạn chỉ có một số ít chức năng).
Giải thích chi tiết hơn về sự khác biệt bạn có thể tìm thấy ở đây: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Trong trường hợp phương thức được đồng bộ hóa, khóa sẽ được lấy trên Object. Nhưng nếu bạn đi với khối được đồng bộ hóa, bạn có một tùy chọn để chỉ định một đối tượng mà khóa sẽ được lấy.
Thí dụ :
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
Tôi biết đây là một câu hỏi cũ, nhưng với việc đọc nhanh các câu trả lời ở đây, tôi thực sự không thấy ai đề cập rằng đôi khi một synchronized
phương pháp có thể là khóa sai .
Từ thực hành đồng thời Java (trang 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
Các mã trên có sự xuất hiện của an toàn chủ đề. Tuy nhiên, trong thực tế thì không. Trong trường hợp này, khóa được lấy theo thể hiện của lớp. Tuy nhiên, danh sách có thể được sửa đổi bởi một luồng khác không sử dụng phương thức đó. Cách tiếp cận đúng sẽ là sử dụng
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
Đoạn mã trên sẽ chặn tất cả các luồng cố gắng sửa đổi danh sách từ sửa đổi danh sách cho đến khi khối được đồng bộ hóa hoàn tất.
List
có thể dẫn đến các vấn đề về hiệu năng nếu có nhật ký mã không nhất thiết phải được đồng bộ hóa
Là một vấn đề thực tế, lợi thế của các phương thức được đồng bộ hóa so với các khối được đồng bộ hóa là chúng có khả năng chống ngốc hơn; bởi vì bạn không thể chọn một đối tượng tùy ý để khóa, bạn không thể sử dụng sai cú pháp phương thức được đồng bộ hóa để thực hiện những điều ngu ngốc như khóa chuỗi ký tự hoặc khóa nội dung của trường có thể thay đổi được thay đổi từ bên dưới các luồng.
Mặt khác, với các phương thức được đồng bộ hóa, bạn không thể bảo vệ khóa khỏi bị thu thập bởi bất kỳ luồng nào có thể có được tham chiếu đến đối tượng.
Vì vậy, sử dụng đồng bộ hóa như một công cụ sửa đổi trên các phương pháp sẽ tốt hơn trong việc bảo vệ những người chăn bò của bạn khỏi bị tổn thương, trong khi sử dụng các khối được đồng bộ hóa kết hợp với các đối tượng khóa riêng cuối cùng sẽ tốt hơn trong việc bảo vệ mã của bạn khỏi các orker bò.
Từ một bản tóm tắt đặc tả Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
Câu lệnh được đồng bộ hóa (§14,17) tính toán một tham chiếu đến một đối tượng; sau đó nó cố gắng thực hiện một hành động khóa trên đối tượng đó và không tiến hành thêm cho đến khi hành động khóa đã hoàn thành thành công. ...
Phương thức được đồng bộ hóa (§8.4.3.5) tự động thực hiện hành động khóa khi được gọi; cơ thể của nó không được thực thi cho đến khi hành động khóa đã hoàn thành. Nếu phương thức là một phương thức cá thể , nó sẽ khóa khóa được liên kết với thể hiện mà nó được gọi (nghĩa là, đối tượng sẽ được gọi là phương thức này trong khi thực thi phần thân của phương thức). Nếu phương thức là tĩnh , nó sẽ khóa khóa được liên kết với đối tượng Class đại diện cho lớp mà phương thức được định nghĩa. ...
Dựa trên những mô tả này, tôi sẽ nói hầu hết các câu trả lời trước đều đúng và một phương thức được đồng bộ hóa có thể đặc biệt hữu ích cho các phương thức tĩnh, trong đó bạn sẽ phải tìm ra cách lấy "Đối tượng lớp đại diện cho lớp trong đó phương thức đó là được xác định. "
Chỉnh sửa: Ban đầu tôi nghĩ đây là những trích dẫn của thông số Java thực tế. Làm rõ rằng trang này chỉ là một bản tóm tắt / giải thích về thông số kỹ thuật
TLDR; Không sử dụng công cụ synchronized
sửa đổi cũng như synchronized(this){...}
biểu thức synchronized(myLock){...}
mà myLock
là trường đối tượng cuối cùng chứa một đối tượng riêng.
Sự khác biệt giữa việc sử dụng công cụ synchronized
sửa đổi trên khai báo phương thức và synchronized(..){ }
biểu thức trong thân phương thức là:
synchronized
cụ sửa đổi được chỉ định trên chữ ký của phương thức
synchronized(this) { .... }
, vàthis
đối tượng là khóa khi khai báo trên phương thức không tĩnh hoặc lớp kèm theo khi khai báo trên phương thức tĩnh.synchronized(...){...}
biểu hiện cho phép bạn
Tuy nhiên, sử dụng công cụ synchronized
sửa đổi hoặc synchronized(...) {...}
với this
đối tượng khóa (như trong synchronized(this) {...}
), có cùng nhược điểm. Cả hai đều sử dụng thể hiện riêng của nó làm đối tượng khóa để đồng bộ hóa. Điều này rất nguy hiểm vì không chỉ bản thân đối tượng mà bất kỳ đối tượng / mã bên ngoài nào có tham chiếu đến đối tượng đó cũng có thể sử dụng nó làm khóa đồng bộ hóa với các tác dụng phụ nghiêm trọng tiềm ẩn (suy giảm hiệu suất và bế tắc ).
Do đó, cách tốt nhất là không sử dụng công cụ synchronized
sửa đổi cũng như synchronized(...)
biểu thức kết hợp với this
đối tượng khóa mà là đối tượng khóa riêng tư đối tượng này. Ví dụ:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
Bạn cũng có thể sử dụng nhiều đối tượng khóa nhưng cần đặc biệt cẩn thận để đảm bảo điều này không dẫn đến bế tắc khi sử dụng lồng nhau.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
Tôi cho rằng câu hỏi này là về sự khác biệt giữa Thread Safe Singleton và Lazy khởi tạo với khóa kiểm tra Double . Tôi luôn đề cập đến bài viết này khi tôi cần thực hiện một số singleton cụ thể.
Chà, đây là một chủ đề an toàn Singleton :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
Ưu điểm:
Lười khởi tạo là có thể.
Đây là chủ đề an toàn.
Nhược điểm:
- Phương thức getInstance () được đồng bộ hóa nên gây ra hiệu năng chậm vì nhiều luồng không thể truy cập đồng thời.
Đây là một khởi tạo lười biếng với khóa kiểm tra kép :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
Ưu điểm:
Lười khởi tạo là có thể.
Nó cũng là chủ đề an toàn.
Hiệu suất giảm vì từ khóa đồng bộ được khắc phục.
Nhược điểm:
Lần đầu tiên, nó có thể ảnh hưởng đến hiệu suất.
Như khuyết điểm. phương pháp khóa kiểm tra kép có thể chịu được để có thể sử dụng cho các ứng dụng đa luồng hiệu suất cao.
Vui lòng tham khảo bài viết này để biết thêm chi tiết:
https://www.geekforgeek.org/java-singleton-design-potype-practices-examples/
Đồng bộ hóa với các chủ đề. 1) KHÔNG BAO GIỜ sử dụng đồng bộ hóa (cái này) trong một chủ đề mà nó không hoạt động. Đồng bộ hóa với (cái này) sử dụng luồng hiện tại làm đối tượng luồng khóa. Vì mỗi luồng độc lập với các luồng khác, nên KHÔNG có sự phối hợp đồng bộ hóa. 2) Các thử nghiệm mã cho thấy rằng trong Java 1.6 trên máy Mac, đồng bộ hóa phương thức không hoạt động. 3) được đồng bộ hóa (lockObj) trong đó lockObj là một đối tượng chia sẻ chung của tất cả các luồng đồng bộ hóa trên nó sẽ hoạt động. 4) ReenterantLock.lock () và .unlock () hoạt động. Xem hướng dẫn Java cho điều này.
Các mã sau đây cho thấy những điểm này. Nó cũng chứa Vector an toàn luồng sẽ được thay thế cho ArrayList, để cho thấy rằng nhiều luồng thêm vào Vector không bị mất bất kỳ thông tin nào, trong khi cùng với ArrayList có thể mất thông tin. 0) Mã hiện tại cho thấy mất thông tin do điều kiện chủng tộc A) Nhận xét dòng A được gắn nhãn hiện tại và bỏ ghi chú dòng A phía trên nó, sau đó chạy, phương thức mất dữ liệu nhưng không nên. B) Đảo ngược bước A, bỏ ghi chú B và // khối kết thúc}. Sau đó chạy để xem kết quả không mất dữ liệu C) Nhận xét B, không ghi chú C. Chạy, xem đồng bộ hóa trên (điều này) mất dữ liệu, như mong đợi. Không có thời gian để hoàn thành tất cả các biến thể, hy vọng điều này sẽ giúp. Nếu đồng bộ hóa trên (cái này) hoặc đồng bộ hóa phương thức hoạt động, vui lòng cho biết phiên bản Java và HĐH nào bạn đã thử nghiệm. Cảm ơn bạn.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class