2012-11-28 38 views
13

Trong khi sử dụng nhiều chủ đề tôi đã học cách sử dụng biến tĩnh bất cứ khi nào tôi muốn sử dụng bộ đếm sẽ được truy cập bởi nhiều luồng.Java - sử dụng AtomicInteger vs Static int

Ví dụ:

static int count=0; Rồi sau này trong chương trình tôi sử dụng nó như count++;.

Hôm nay tôi bắt gặp một cái gì đó gọi là AtomicInteger và tôi cũng biết rằng đó là Chủ đề an toàn và có thể sử dụng một trong các phương pháp được gọi là getAndInrement() để đạt được hiệu quả tương tự.

Có ai giúp tôi hiểu về việc sử dụng static atomicInteger so với static int count không?

+0

Bạn phải bảo vệ truy cập đồng thời đến một 'int' mình. Tuy nhiên, 'AtomicInteger' được thiết kế để an toàn chỉ. – reprogrammer

+9

'static' không có liên quan gì đến đa luồng. –

+0

Theo hiểu biết của tôi, chúng tôi sử dụng biến biến động thay vì biến tĩnh trong môi trường đa luồng. – Vishal

Trả lời

20

-AtomicInteger được sử dụng để thực hiện các hoạt động nguyên tử qua một số nguyên, một sự thay thế của nó khi bạn không muốn sử dụng synchronized từ khóa.

- Sử dụng volatile trên trường không phải nguyên tử sẽ cho kết quả không nhất quán .

int volatile count; 

public void inc(){ 

count++ 

} 

-static sẽ làm cho một biến chia sẻ bởi tất cả các trường hợp mà lớp, Nhưng vẫn nó sẽ tạo ra một kết quả không phù hợp trong môi trường đa luồng.

Vì vậy, hãy thử những khi bạn đang ở trong môi trường đa luồng:

1. của nó luôn luôn tốt hơn để làm theo Quy tắc của Brian:

Khi bao giờ chúng ta viết một biến mà là bên cạnh để được đọc bởi một chủ đề khác là hoặc khi chúng tôi đọc một biến được viết chỉ bằng một chuỗi khác, nó cần được đồng bộ hóa. Các trường được chia sẻ phải là được đặt ở chế độ riêng tư, làm cho các phương pháp đọc và viết/báo cáo nguyên tử được đồng bộ hóa .

2. tùy chọn thứ hai là sử dụng Atomic Classes, như AtomicInteger, AtomicLong, AtomicReference, etc.

+0

Cảm ơn Kumar !!!!! – user547453

+0

@ user547453 Bạn được chào đón ........... –

1

Với AtomicInteger số incrementAndGet() được đảm bảo là nguyên tử.
Nếu bạn sử dụng count++ để nhận giá trị trước đó, nó không được đảm bảo là nguyên tử.


Điều gì đó tôi đã bỏ lỡ câu hỏi của bạn - và được tuyên bố bằng câu trả lời khác - tĩnh không liên quan gì đến luồng.

+0

ý của bạn là gì? – user547453

+2

Xem http://en.wikipedia.org/wiki/Atomicity_(programming) – reprogrammer

+1

nó có nghĩa là, ví dụ, nếu hai luồng đang sử dụng, ví dụ 'prevCount = count ++' cùng một lúc, có thể là cả hai thread sẽ nhận được cùng một giá trị của prevCount. –

1

"tĩnh" làm cho biến là cấp lớp. Điều đó có nghĩa, nếu bạn định nghĩa "static int count" trong một lớp, cho dù bạn tạo ra bao nhiêu trường hợp, tất cả các cá thể đều sử dụng cùng một "count". Trong khi AtomicInteger là một lớp bình thường, nó chỉ thêm bảo vệ đồng bộ hóa.

1

static int counter sẽ cho bạn kết quả không nhất quán trong môi trường multithreaded trừ khi bạn thực hiện bộ đếm volatile hoặc tạo khối gia tăng synchronized.

Trong trường hợp automic, nó cung cấp cho lock-freethread-safe lập trình trên các biến duy nhất.

Xem chi tiết tại automic'slink

+1

Tôi không nghĩ rằng dễ bay hơi là đủ để làm cho ++ an toàn. –

1

Tôi nghĩ rằng không có bảo lãnh để xem trên count++ giá trị mới nhất. count++ phải đọc giá trị của count. Một Thread khác có thể đã viết một giá trị mới cho count nhưng đã lưu trữ giá trị của nó trên bộ nhớ cache cục bộ Thread, i. e. không tuôn ra bộ nhớ chính. Ngoài ra, Thread của bạn, mà đọc count, không có gurantee để đọc từ bộ nhớ chính, i. e. làm mới từ bộ nhớ chính. synchronize gurantees đó.

+0

Cảm ơn Vertex !!!!! – user547453

1

AtomicInteger là làm cho việc nhận và tăng lên như là một quá trình nguyên tử. Nó có thể được coi là một Sequencer trong cơ sở dữ liệu. Nó cung cấp các phương thức tiện ích để tăng, giảm giá trị int delta.

static int có thể gây ra vấn đề nếu bạn đang nhận được truy cập và sau đó xử lý và sau đó cập nhật nó. AtomicInteger thực hiện dễ dàng nhưng bạn không thể sử dụng nó nếu bạn phải cập nhật bộ đếm dựa trên kết quả xử lý.

+0

Cảm ơn Pankaj !!!!! – user547453

5

Tôi đồng ý với câu trả lời @ Kumar.

Dễ bay hơi là không đủ - nó có một số ý nghĩa đối với thứ tự bộ nhớ, nhưng không đảm bảo nguyên tử của ++.

Điều thực sự khó khăn về lập trình đa luồng là các sự cố có thể không hiển thị trong bất kỳ số lượng kiểm tra hợp lý nào. Tôi đã viết một chương trình để chứng minh vấn đề, nhưng nó có chủ đề mà không làm gì ngoài quầy tăng dần. Mặc dù vậy, số lượng nằm trong khoảng 1% câu trả lời đúng. Trong một chương trình thực sự, trong đó các luồng có công việc khác để làm, có thể có một xác suất rất thấp của hai luồng làm ++ đóng đủ để đồng thời hiển thị vấn đề. Không thể kiểm tra độ chính xác đa luồng, nó phải được thiết kế.

Chương trình này thực hiện cùng một nhiệm vụ đếm bằng cách sử dụng một int tĩnh đơn giản, int dễ bay hơi và AtomicInteger. Chỉ AtomicInteger liên tục nhận được câu trả lời đúng. Một sản lượng điển hình trên một đa với 4 lõi kép ren là:

count: 1981788 volatileCount: 1982139 atomicCount: 2000000 Expected count: 2000000 

Dưới đây là mã nguồn:

import java.util.ArrayList; 
    import java.util.List; 
    import java.util.concurrent.atomic.AtomicInteger; 

    public class Test { 
    private static int COUNTS_PER_THREAD = 1000000; 
    private static int THREADS = 2; 

    private static int count = 0; 
    private static volatile int volatileCount = 0; 
    private static AtomicInteger atomicCount = new AtomicInteger(); 

    public static void main(String[] args) throws InterruptedException { 
     List<Thread> threads = new ArrayList<Thread>(THREADS); 
     for (int i = 0; i < THREADS; i++) { 
     threads.add(new Thread(new Counter())); 
     } 
     for (Thread t : threads) { 
     t.start(); 
     } 
     for (Thread t : threads) { 
     t.join(); 
     } 
     System.out.println("count: " + count + " volatileCount: " + volatileCount + " atomicCount: " 
      + atomicCount + " Expected count: " 
      + (THREADS * COUNTS_PER_THREAD)); 
    } 

    private static class Counter implements Runnable { 
     @Override 
     public void run() { 
     for (int i = 0; i < COUNTS_PER_THREAD; i++) { 
      count++; 
      volatileCount++; 
      atomicCount.incrementAndGet(); 
     } 
     } 
    } 
    } 
+0

Cảm ơn Patricia .... Tôi hiểu việc sử dụng atomicInteger khi tôi phải sử dụng bộ đếm như được nêu trong câu hỏi ban đầu của mình. Nhưng nếu tôi có 3 chủ đề đồng bộ cập nhật giá trị của biến như ThreadA, ThreadB và ThreadC (ví dụ). Trong trường hợp này (như Kumar đề xuất), tôi phải sử dụng một phương thức đồng bộ riêng để đọc và ghi giá trị. Và cũng làm cho biến của tôi được định nghĩa là 'Static Volatile'. Ví dụ: 'chuỗi tĩnh ổn định riêng threadName =" ThreadX ";' – user547453

+0

@ user547453 Nếu tất cả các truy cập vào một biến được đồng bộ hóa trên cùng một đối tượng, không cần phải làm cho nó biến động. Làm như vậy sẽ không có hiệu lực hoặc làm chậm mã của bạn một cách không cần thiết. Có nhiều cách để tạo một biến được chia sẻ giữa nhiều luồng - nó không phải là tĩnh. –

+0

Một điều quan trọng cần lưu ý ở đây: '++' không phải là một hoạt động nguyên tử - nó là viết tắt của 'a = a + 1', bao gồm đọc giá trị và viết giá trị. Tuy nhiên đọc hoặc thiết lập giá trị IS là một hoạt động nguyên tử trên một trường dễ bay hơi. – mucaho