tìm hiểu thread trong java – phần 5

14
19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog 1/14 https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/ M obilesprogramming's Blog JAVA | SYMBIAN | ANDROID Tìm hiểu Thread trong JAVA – Phần 5 Bài 4: Đồng bộ hóa(Synchronized) Thread (tiếp theo) 3.Static synchronized method: Ngoài lock ở cấp độ đối tượng(object-level) cho mỗi instance của lớp, còn có lock ở cấp độ lớp (class- level) được chia sẽ cho tất cả instance của một lớp cụ thể. Mỗi lớp nạp bởi JavaVM có đúng một lock class-level. Nếu một phương thức được khai báo cả 2 từ khóa static và synchronized, một thread phải có được quyền truy cập vào lock class-level trước khi đi vào phương thức. Lock class-level có thể được sử dụng để truy cập độc quyền vào các biến thành viên static. Cũng như lock object-level cần được ngăn chặn sự sửa đổi dữ liệu của các biến thành viên non-static, lock class-level cũng cần được ngăn chặn sự sửa đổi của các biến thành viên static. Thậm chí là khi không có biến nào liên quan, thì modifier synchronized cũng có thể được sử dụng trên các phương thức static nhằm đảm bảo chỉ có một thread nằm bên trong một phương thức tại một thời điểm. Để hiểu rõ thêm về vấn đề này, ta xét ví dụ sau: 1: public class StaticNeedSync extends Object { 2: private static int nextSerialNum = 10001; 3: 4: public static int getNextSerialNum() { 5: int sn = nextSerialNum; 6: 7: // Simulate a delay that is possible if the thread 8: // scheduler chooses to swap this thread off the 9: // processor at this point. The delay is exaggerated

Upload: chung-bao-nguyen

Post on 18-Apr-2015

166 views

Category:

Documents


4 download

DESCRIPTION

đa luồng trong java

TRANSCRIPT

Page 1: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

1/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

Mobilesprogramming's Blog

JAVA | SYMBIAN | ANDROID

Tìm hiểu Thread trong JAVA – Phần 5

Bài 4: Đồng bộ hóa(Synchronized) Thread (tiếp theo)

3.Static synchronized method:

Ngoài lock ở cấp độ đối tượng(object-level) cho mỗi instance của lớp, còn có lock ở cấp độ lớp (class-level) được chia sẽ cho tất cả instance của một lớp cụ thể. Mỗi lớp nạp bởi JavaVM có đúng một lock

class-level. Nếu một phương thức được khai báo cả 2 từ khóa static và synchronized, một threadphải có được quyền truy cập vào lock class-level trước khi đi vào phương thức.

Lock class-level có thể được sử dụng để truy cập độc quyền vào các biến thành viên static. Cũngnhư lock object-level cần được ngăn chặn sự sửa đổi dữ liệu của các biến thành viên non-static, lock

class-level cũng cần được ngăn chặn sự sửa đổi của các biến thành viên static. Thậm chí là khikhông có biến nào liên quan, thì modifier synchronized cũng có thể được sử dụng trên các phương

thức static nhằm đảm bảo chỉ có một thread nằm bên trong một phương thức tại một thời điểm.

Để hiểu rõ thêm về vấn đề này, ta xét ví dụ sau:

1: public class StaticNeedSync extends Object {

2: private static int nextSerialNum = 10001;

3:

4: public static int getNextSerialNum() {

5: int sn = nextSerialNum;

6:

7: // Simulate a delay that is possible if the thread

8: // scheduler chooses to swap this thread off the

9: // processor at this point. The delay is exaggerated

Page 2: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

2/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

10: // for demonstration purposes.

11: try { Thread.sleep(1000); }

12: catch ( InterruptedException x ) { }

13:

14: nextSerialNum++;

15: return sn;

16: }

17:

18: private static void print(String msg) {

19: String threadName = Thread.currentThread().getName();

20: System.out.println(threadName + “: “ + msg);

21: }

22:

23: public static void main(String[] args) {

24: try {

25: Runnable r = new Runnable() {

26: public void run() {

27: print(“getNextSerialNum()=” +

28: getNextSerialNum());

29: }

30: };

31:

32: Thread threadA = new Thread(r, “threadA”);

33: threadA.start();

34:

Page 3: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

3/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

35: Thread.sleep(1500);

36:

37: Thread threadB = new Thread(r, “threadB”);

38: threadB.start();

39:

40: Thread.sleep(500);

41:

42: Thread threadC = new Thread(r, “threadC”);

43: threadC.start();

44:

45: Thread.sleep(2500);

46:

47: Thread threadD = new Thread(r, “threadD”);

48: threadD.start();

49: } catch ( InterruptedException x ) {

50: // ignore

51: }

52: }

53: }

Lớp StaticNeedSync có một biến thành viên nextSerialNum với khai báo private static, được dùng

để lưu giá trị các số tiếp theo sẽ được tạo ra (dòng 2). Phương thức getNextSerialNum () (dòng 4-16) là được khai báo public và static. Khi gọi phương thức này, nó có giá trị hiện tại của

nextSerialNum và lưu trữ nó trong một biến cục bộ sn (dòng 5). Sau đó, thread gọi phương thức

này tạm sleep 1 giây(dòng 11) để một thread thứ 2 thực hiện. Khi các thread nhận được một cơ hộiđể chạy lại, nó tăng nextSerialNum lên 1 biến để chuẩn bị cho cuộc gọi tiếp theo (dòng 14).

Main thread có 4 thread tương tác với phương thức getNextSerialNum(). Cả 4 thread đều sử dụng

cùng một đối tượng Runnable(dòng 25-30). Main thread bắt đầu với threadA(dòng 33) và sleepkhoảng 1,5 giây. Thời gian này đủ để threadA vào và trả về từ phương thức getNextSerialNum().

Tiếp theo, main Thread tiếp tục bắt đầu với threadB(dòng 38), sau đó nó sleep khoảng 0,5 giây(dòng

Page 4: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

4/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

40) trước khi nó bắt đầu threadC(dòng 43). Cả threadB và threadC cùng nằm trong phương thức

getNextSerialNum(), và điều này làm nảy sinh một vài vấn đề. Sau khi chờ 2,5 giây (nhiều thời giancho threadB và threadC trả về), main thread bắt đầu threadD (dòng 45-48). threadD gọi phương

thức getNextSerialNum () lần cuối cùng. Kết quả dưới đây mô tả ví dụ trên:

threadA: getNextSerialNum()=10001

threadB: getNextSerialNum()=10002

threadC: getNextSerialNum()=10002

threadD: getNextSerialNum()=10004

Để giải quyết vấn đề trên, ta chỉ cần thêm vào từ khóa synchronized vào phương thứcgetNextSerialNum().

1: public class StaticSync extends Object {

2: private static int nextSerialNum = 10001;

3:

4: public static synchronized int getNextSerialNum() {

5: int sn = nextSerialNum;

6:

7: // Simulate a delay that is possible if the thread

8: // scheduler chooses to swap this thread off the

9: // processor at this point. The delay is exaggerated

10: // for demonstration purposes.

11: try { Thread.sleep(1000); }

12: catch ( InterruptedException x ) { }

13:

14: nextSerialNum++;

15: return sn;

16: }

17:

Page 5: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

5/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

18: private static void print(String msg) {

19: String threadName = Thread.currentThread().getName();

20: System.out.println(threadName + “: “ + msg);

21: }

22:

23: public static void main(String[] args) {

24: try {

25: Runnable r = new Runnable() {

26: public void run() {

27: print(“getNextSerialNum()=” +

28: getNextSerialNum());

29: }

30: };

31:

32: Thread threadA = new Thread(r, “threadA”);

33: threadA.start();

34:

35: Thread.sleep(1500);

36:

37: Thread threadB = new Thread(r, “threadB”);

38: threadB.start();

39:

40: Thread.sleep(500);

41:

42: Thread threadC = new Thread(r, “threadC”);

Page 6: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

6/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

43: threadC.start();

44:

45: Thread.sleep(2500);

46:

47: Thread threadD = new Thread(r, “threadD”);

48: threadD.start();

49: } catch ( InterruptedException x ) {

50: // ignore

51: }

52: }

53: }

Kết quả từ ví dụ trên:

threadA: getNextSerialNum()=10001

threadB: getNextSerialNum()=10002

threadC: getNextSerialNum()=10003

threadD: getNextSerialNum()=10004

Sở dĩ vấn đề trên được giải quyết là do khi threadC vào phương thức getNextSerialNum(), nó lậptức rơi vào trạng thái block và đợi threadB kết thúc nó mới được phép tiếp tục thực hiện trongphương thức getNextSerialNum().

4.Sử dụng Class-level lock trong synchronized statement.

Để sử dụng synchronized statement trong Class-level lock, ta sử dụng theo cú pháp sau:

synchronized ( ClassName.class ) {

// body

}

Ví dụ: ta tạo lớp StaticBlock để mô phỏng kỹ thuật trên như sau:

1: public class StaticBlock extends Object {

Page 7: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

7/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

2: public static synchronized void staticA() {

3: System.out.println(“entering staticA()”);

4:

5: try { Thread.sleep(5000); }

6: catch ( InterruptedException x ) { }

7:

8: System.out.println(“leaving staticA()”);

9: }

10:

11: public static void staticB() {

12: System.out.println(“entering staticB()”);

13:

14: synchronized ( StaticBlock.class ) {

15: System.out.println(

16: “in staticB() – inside sync block”);

17:

18: try { Thread.sleep(2000); }

19: catch ( InterruptedException x ) { }

20: }

21:

22: System.out.println(“leaving staticB()”);

23: }

24:

25: public static void main(String[] args) {

26: Runnable runA = new Runnable() {

Page 8: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

8/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

27: public void run() {

28: StaticBlock.staticA();

29: }

30: };

31:

32: Thread threadA = new Thread(runA, “threadA”);

33: threadA.start();

34:

35: try { Thread.sleep(200); }

36: catch ( InterruptedException x ) { }

37:

38: Runnable runB = new Runnable() {

39: public void run() {

40: StaticBlock.staticB();

41: }

42: };

43:

44: Thread threadB = new Thread(runB, “threadB”);

45: threadB.start();

46: }

47: }

Trong lớp StaticBlock, phương thức staticA() được khai báo synchronized và static. Phương thứcstaticB() được khai báo static và có chứa một block synchronized(dòng 14-20). Các đối tượng sửdụng để kiểm soát truy cập vào block này là đối tượng Class cho StaticBlock và được tìm thấy bằng

cách sử StaticBlock.class(dòng 14).

Trong phương thức main, threadA được bắt đầu và gọi phương thức staticA()(dòng 28). Sau một

khoảng thời gian 200 mili giây, threadB bắt đầu và gọi phương thức staticB(). Trong khi threadAsleep trong phương thức staticA(), threadB vào phương thức staticB(), in một message và đi vào

Page 9: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

9/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

block static synchronized(dòng 14).

Khi threadA trả về từ staticA(), threadB nhận class-level lock và hoàn tất phương thức staticB().

Sau đây là kết quả của ví dụ trên:

1: entering staticA()

2: entering staticB()

3: leaving staticA()

4: in staticB() – inside sync block

5: leaving staticB()

Lưu ý rằng, mặc dù threadB có thể vào phương thức staticB()(dòng 2) nhưng nó không thể vào để

thực hiện block synchronized(dòng 4) cho đến khi threadA trả về từ staticA()(dòng 3). ThreadB sẽrơi vào trạng thái block cho tới khi threadA giải phóng class-level lock.

5.Deadlocks:

Khi sử dụng nhiều thread truy cập vào các đối tượng có giữ lock, nếu không cẩn thận thì rất có thểxảy ra tình trạng deadlocks. Đó là khi một threadA nắm giữ lock1, một threadB nắm giữ lock2.

Trong khi threadA nắm giữ lock1, nó lại muốn nắm giữ thêm lock2, nhưng vì threadB đang nắmgiữ lock2 nên threadA sẽ rơi vào trạng thái block, và nó phải đợi cho tới khi threadB giải phónglock2. Tuy nhiên, vào thời điểm đó, trong khi threadB đang nắm giữ lock2, nó lại muốn tiếp tụcnắm giữ lock1, và lock1 đang được nắm giữ bởi threadA, nên threadB lại rơi vào trạng thái block.

Lúc này, cả threadA và threadB rơi vào trạng thái block mãi mãi vì phải đợi thread kia giải phónglock. Đây chính là trường hợp deadlocks.

Ví dụ:

1: public class Deadlock extends Object {

2: private String objID;

3:

4: public Deadlock(String id) {

5: objID = id;

6: }

7:

8: public synchronized void checkOther(Deadlock other) {

Page 10: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

10/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

9: print(“entering checkOther()”);

10:

11: // simulate some lengthy process

12: try { Thread.sleep(2000); }

13: catch ( InterruptedException x ) { }

14:

15: print(“in checkOther() – about to “ +

16: “invoke ‘other.action()’”);

17: other.action();

18:

19: print(“leaving checkOther()”);

20: }

21:

22: public synchronized void action() {

23: print(“entering action()”);

24:

25: // simulate some work here

26: try { Thread.sleep(500); }

27: catch ( InterruptedException x ) { }

28:

29: print(“leaving action()”);

30: }

31:

32: public void print(String msg) {

33: threadPrint(“objID=” + objID + “ – “ + msg);

Page 11: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

11/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

34: }

35:

36: public static void threadPrint(String msg) {

37: String threadName = Thread.currentThread().getName();

38: System.out.println(threadName + “: “ + msg);

39: }

40:

41: public static void main(String[] args) {

42: final Deadlock obj1 = new Deadlock(“obj1”);

43: final Deadlock obj2 = new Deadlock(“obj2”);

44:

45: Runnable runA = new Runnable() {

46: public void run() {

47: obj1.checkOther(obj2);

48: }

49: };

50:

51: Thread threadA = new Thread(runA, “threadA”);

52: threadA.start();

53:

54: try { Thread.sleep(200); }

55: catch ( InterruptedException x ) { }

56:

57: Runnable runB = new Runnable() {

58: public void run() {

Page 12: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

12/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

59: obj2.checkOther(obj1);

60: }

61: };

62:

63: Thread threadB = new Thread(runB, “threadB”);

64: threadB.start();

65:

66: try { Thread.sleep(5000); }

67: catch ( InterruptedException x ) { }

68:

69: threadPrint(“finished sleeping”);

70:

71: threadPrint(“about to interrupt() threadA”);

72: threadA.interrupt();

73:

74: try { Thread.sleep(1000); }

75: catch ( InterruptedException x ) { }

76:

77: threadPrint(“about to interrupt() threadB”);

78: threadB.interrupt();

79:

80: try { Thread.sleep(1000); }

81: catch ( InterruptedException x ) { }

82:

83: threadPrint(“did that break the deadlock?”);

Page 13: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

13/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

84: }

85: }

Trong phương thức main(), có hai instance của Deadlock được tạo ra và hai thread được bắt đầu,mỗi thread chạy trên một instance; instance thứ nhất được thiết lập là obj1, cái kia là obj2(dòng 42-43). ThreadA được start và gọi phương thức checkOther() của obj1 và gán một tham chiếu đếnobj2(dòng 47), sau khi sleep khoảng 200 mili giây, threadB được start và gọi phương thứccheckOther() của obj2 và nó gán một tham chiếu tới obj1. Phương thức checkOther() làsynchronized nên thread truy cập vào nó sẽ nhận lock. Cả threadA và threadB sẽ không bao giờ

thực hiện được phương thức action(), vì khi threadA đang ở trong phương thức checkOther() nónắm giữ lock trên obj1, thì threadB cũng ở trong phương thức checkOther() và nắm giữ lock củaobj2. Khi threadA cố gắng gọi phương thức action() trong obj2, nó sẽ bị block do threadB đang nắmgiữ lock trên obj2, và nó sẽ đợi cho tới khi threadB giải phóng lock trên obj2. Sau đó, threadB gọiphương thức action() trên obj1 và cũng rơi vào trạng thái block do lock trên obj1 đã bị threadAchiếm giữ; main thread sleep trong 5 giây khi deadlock được tạo ra(dòng 66), khi tỉnh dậy, nó cốgắng phá vỡ deadlock bằng cách interrupt threadA(dòng 72), sau 1 giây nó tiếp tục cố gắng

interrupt threadB. Thật không may, điều này không phá vỡ deadlock, bởi khi một thread đang bịblock vì đợi để có được lock, nó không đáp ứng với interrupt.

Kết quả từ ví dụ trên:

threadA: objID=obj1 – entering checkOther()

threadB: objID=obj2 – entering checkOther()

threadA: objID=obj1 – in checkOther() – about to invoke ‘other.action()’

threadB: objID=obj2 – in checkOther() – about to invoke ‘other.action()’

main: finished sleeping

main: about to interrupt() threadA

main: about to interrupt() threadB

main: did that break the deadlock?

Bạn thấy bài viết này thế nào?

Các bài liên quan:Tìm hiểu Thread trong Java – Phần 4

08/12/2010 mobilesprogrammingCategories: Thread Tags: java thread

Page 14: Tìm hiểu Thread trong JAVA – Phần 5

19/07/2012 Tìm hiểu Thread trong JAVA – Phần 5 « Mobilesprogramming's Blog

14/14https://mobilesprogramming.wordpress.com/2010/08/12/tim-hiểu-thread-trong-java-phần-5/

3 responses to “Tìm hiểu Thread trong JAVA –Phần 5”

Huang

August 23rd, 2010 at 22:17

Để giải quyết vấn đề khi mà trường hợp deadlock xảy ra phải làm thế nào bạn ơi?

Reply

mobilesprogramming August 24th, 2010 at 14:18

Chào bạn!Có rất nhiều cách để hạn chế tình trạng Deadlock, tuy nhiên có 2 cách thường hay sử dụng:- Nắm giữ lock của đối tượng trong thời gian càng ít càng tốt, cách này thường sử dụngsynchronized statement block để thay thế cho sychronized method.- Thiết kế hệ thống sao cho không nắm giữ nhiều hơn một lock tại một thời điểm.Trong ví dụ trên, ở dụng 17, nếu bạn thay thế bằng:

this.action();thì sẽ hạn chế được tình trạng Deadlock.

Reply

Gin April 19th, 2011 at 17:03

Thanks so much! The articles are very helpful and helped me a lot. I hope you will write more inthe future.Thanks again !

Reply

Blog at WordPress.com. | Theme: Dark Wood by Nischal Maniar.