21. 实现多线程的两种方法:Thread与Runable



在Java中实现多线程编程有以下几个方法:



1.继承Thread类,重写run方法



[java]  view plain  copy

 


1. public class Test {  
2.       
3. public static void main(String[] args) {  
4. new MyThread().start();  
5.     }  
6.       
7. private static class MyThread extends Thread {  
8. @Override  
9. public void run() {  
10. "run!");  
11.         }  
12.     }  
13.       
14. }

 



2.实现Runnable接口,作为参数传入Thread构造函数



[java]  view plain  copy

 

1. public class Test {  
2.       
3. public static void main(String[] args) {  
4. new Thread(new Runnable() {  
5.               
6. @Override  
7. public void run() {  
8. "run!");  
9.                   
10.             }  
11.         }).start();  
12.     }  
13.       
14. }



3.使用ExecutorService类



[java]  view plain  copy

 


1. import java.util.concurrent.ExecutorService;  
2. import java.util.concurrent.Executors;  
3.   
4. public class Test {  
5.       
6. public static void main(String[] args) {  
7.         ExecutorService service = Executors.newCachedThreadPool();  
8. new Runnable() {  
9.               
10. @Override  
11. public void run() {  
12. "Run!");  
13.             }  
14.         });  
15.     }  
16.       
17. }

22. 线程同步的方法:sychronized、lock、reentrantLock等



多线程编程时同步一直是一个非常重要的问题,很多时候我们由于同步问题导致程序失败的概率非常低,往往存在我们的代码缺陷,但他们看起来是正确的:



[java]  view plain  copy

 


1. public class Test {  
2.       
3. private static int value = 0;  
4.       
5. public static void main(String[] args) {  
6. new Test();  
7. // 创建两个线程  
8. new MyThread();  
9. new MyThread();  
10.         thread1.start();  
11.         thread2.start();  
12.     }  
13.       
14. /** 
15.      * 为静态变量value加2 
16.      * @return 
17.      */  
18. public int next() {  
19.         value++;  
20. // 加速问题的产生  
21.         value++;  
22. return value;  
23.     }  
24.       
25. /** 
26.      * 判断是否偶数 
27.      * @param num 
28.      * @return boolean 是否偶数 
29.      */  
30. public boolean isEven(int num) {  
31. return num % 2 == 0;  
32.     }  
33.       
34. class MyThread extends Thread {  
35. @Override  
36. public void run() {  
37. " start!");  
38. while(isEven(next()));  
39. " down!");  
40.         }  
41.     }  
42.       
43. }

上面的代码创建了两个线程操作Test类中的静态变量value,调用next方法每次会为value的值加2,理论上来说isEven方法的返回值应该总是true,两个线程的工作会不停止的执行下去。但事实是:




java 如何获得gc java获得class四种方法_System


因此在我们进行多线程并发编程时,使用同步技术是非常重要的。


1.synchronized


Java以提供关键字synchronized的形式,为防止资源冲突提供了内置支持。当某个线程处于一个对于标记为synchronized的方法的调用中,那么在这个线程从方法返回前,其他所有要调用类中任何标记为synchronized方法的线程都会被阻塞。对刚才的代码稍作修改,如下:


[java]  view plain  copy

 

1. /** 
2.      * 为静态变量value加2 
3.      * @return 
4.      */  
5. public synchronized int next() {  
6.         value++;  
7. // 加速问题的产生  
8.         value++;  
9. return value;  
10.     }

 


除了锁定方法,synchronized关键字还能锁定固定代码块:


[java]  view plain  copy

 


1. /** 
2.      * 为静态变量value加2 
3.      *  
4.      * @return 
5.      */  
6. public int next() {  
7. synchronized (this) {  
8.             value++;  
9. // 加速问题的产生  
10.             value++;  
11. return value;  
12.         }  
13.     }


在synchronized关键字后的小括号内加入要加锁的对象即可。通过这种方法分离出来的代码段被称为临界区,也叫作同步控制块。

 


java 如何获得gc java获得class四种方法_java_02


2.ReentrantLock


除了synchronized关键字外,我们还可以使用Lock对象为我们的代码加锁,Lock对象必须被显示地创建、锁定和释放:


[java]  view plain  copy

 


1. private static Lock lock = new ReentrantLock();  
2. /** 
3.      * 为静态变量value加2 
4.      * @return 
5.      */  
6. public int next() {  
7.         lock.lock();  
8. try {  
9.             value++;  
10. // 加速问题的产生  
11.             value++;  
12. return value;  
13. finally {  
14.             lock.unlock();  
15.         }  
16.     }


[java]  view plain  copy

 

1. /** 
2.      * 为静态变量value加2 
3.      * @return 
4.      */  
5. public int next() {  
6. boolean getLock = lock.tryLock();  
7. if (getLock) {  
8. try {  
9.                 value++;  
10. // 加速问题的产生  
11.                 value++;  
12. return value;  
13. finally {  
14.                 lock.unlock();  
15.             }  
16. else {  
17. // do something else  
18. "say : I don't get the lock, QAQ");  
19. return 0;  
20.         }  
21.     }

 


23. 锁的等级:对象锁、类锁


这是关于synchronized关键字的概念,synchronized关键字可以用来锁定对象的非静态方法或其中的代码块,此时关键字是为对象的实例加锁了,所以称为对象锁:


[java]  view plain  copy

 

1. public synchronized void f() {};  
2. public void g() {  
3. synchronized (this) {  
4.               
5.         }  
6.     }

[java]  view plain  copy

 


1. public class Test {  
2. public static synchronized void f() {};  
3. public static void g() {  
4. synchronized (Test.class) {  
5.               
6.         }  
7.     }  
8. }



24. 写出生产者消费者模式


生产者消费者模式一般而言有四种实现方法:

1. wait和notify方法
2. await和signal方法
3. BlockingQueue阻塞队列方法
4. PipedInputStream和PipedOutputStream管道流方法

第一种方法(wait和notify)的实现:


[java]  view plain  copy

 


1. import java.util.LinkedList;  
2. import java.util.Queue;  
3.   
4. class MyQueue {  
5.     Queue<Integer> q;  
6. int size; // 队列持有产品数  
7. final int MAX_SIZE = 5; // 队列最大容量  
8.   
9. public MyQueue() {  
10. new LinkedList<>();  
11. 0;  
12.     }  
13.   
14. /** 
15.      * 生产产品 
16.      *  
17.      * @param num 
18.      *            产品号码 
19.      */  
20. public synchronized void produce(int num) {  
21. // 容量不足时,等待消费者消费  
22. try {  
23. while (size > MAX_SIZE)  
24.                 wait();  
25. catch (InterruptedException e) {  
26.         }  
27.         ;  
28. "produce " + num);  
29.         q.add(num);  
30.         size++;  
31. // 提醒消费者消费  
32.         notifyAll();  
33.     }  
34.       
35. /** 
36.      * 消费产品 
37.      */  
38. public synchronized void comsume() {  
39. // 没有产品时,等待生产  
40. try {  
41. while (size < 1)  
42.                 wait();  
43. catch (InterruptedException e) {  
44.         }  
45.         ;  
46. "comsume " + q.poll());  
47.         size--;  
48. // 提醒生产者生产  
49.         notifyAll();  
50.     }  
51. }  
52.   
53. class Producer extends Thread {  
54. private MyQueue q;  
55.   
56. public Producer(MyQueue q) {  
57. this.q = q;  
58.     }  
59.   
60. @Override  
61. public void run() {  
62. for (int i = 0; i < 10; i++)  
63.             q.produce(i);  
64.     }  
65. }  
66.   
67. class Consumer extends Thread {  
68. private MyQueue q;  
69.   
70. public Consumer(MyQueue q) {  
71. this.q = q;  
72.     }  
73.   
74. @Override  
75. public void run() {  
76. for (int i = 0; i < 10; i++)  
77.             q.comsume();  
78.     }  
79. }  
80.   
81. public class Test {  
82. public static void main(String[] args) {  
83. new MyQueue();  
84. new Producer(q);  
85. new Consumer(q);  
86.         producer.start();  
87.         consumer.start();  
88.     }  
89. }


 


第二种方法(await和signal)实现:


[java]  view plain  copy

1. import java.util.LinkedList;  
2. import java.util.Queue;  
3. import java.util.concurrent.locks.Condition;  
4. import java.util.concurrent.locks.Lock;  
5. import java.util.concurrent.locks.ReentrantLock;  
6.   
7. class MyQueue {  
8.     Queue<Integer> q;  
9. int size; // 队列持有产品数  
10. final int MAX_SIZE = 5; // 队列最大容量  
11. private Lock lock; // 锁  
12. private Condition condition; // 条件变量  
13.   
14. public MyQueue() {  
15. new LinkedList<>();  
16. 0;  
17. new ReentrantLock();  
18.         condition = lock.newCondition();  
19.     }  
20.   
21. /** 
22.      * 生产产品 
23.      *  
24.      * @param num 
25.      *            产品号码 
26.      */  
27. public void produce(int num) {  
28. // 进入临界区上锁  
29.         lock.lock();  
30. // 容量不足时,等待消费者消费  
31. try {  
32. while (size > MAX_SIZE)  
33.                 condition.await();  
34. catch (InterruptedException e) {  
35.             e.printStackTrace();  
36.         };  
37. "produce " + num);  
38.         q.add(num);  
39.         size++;  
40. // 提醒消费者消费  
41.         condition.signalAll();  
42. // 退出临界区解锁  
43.         lock.unlock();  
44.     }  
45.   
46. /** 
47.      * 消费产品 
48.      */  
49. public void comsume() {  
50. // 上锁进入临界区  
51.         lock.lock();  
52. // 没有产品时,等待生产  
53. try {  
54. while (size < 1)  
55.                 condition.await();  
56. catch (InterruptedException e) {  
57.             e.printStackTrace();  
58.         };  
59. "comsume " + q.poll());  
60.         size--;  
61. // 提醒生产者生产  
62.         condition.signalAll();  
63. // 退出临界区解锁  
64.         lock.unlock();  
65.     }  
66. }  
67.   
68. class Producer extends Thread {  
69. private MyQueue q;  
70.   
71. public Producer(MyQueue q) {  
72. this.q = q;  
73.     }  
74.   
75. @Override  
76. public void run() {  
77. for (int i = 0; i < 10; i++)  
78.             q.produce(i);  
79.     }  
80. }  
81.   
82. class Consumer extends Thread {  
83. private MyQueue q;  
84.   
85. public Consumer(MyQueue q) {  
86. this.q = q;  
87.     }  
88.   
89. @Override  
90. public void run() {  
91. for (int i = 0; i < 10; i++)  
92.             q.comsume();  
93.     }  
94. }  
95.   
96. public class Main {  
97. public static void main(String[] args) {  
98. new MyQueue();  
99. new Producer(q);  
100. new Consumer(q);  
101.         producer.start();  
102.         consumer.start();  
103.     }  
104. }

 


[java]  view plain  copy

 

1. import java.util.concurrent.BlockingQueue;  
2. import java.util.concurrent.LinkedBlockingQueue;  
3.   
4. class MyQueue {  
5. // 阻塞队列  
6. int size; // 队列持有产品数(此例无用)  
7. final int MAX_SIZE = 5; // 队列最大容量  
8.   
9. public MyQueue() {  
10. new LinkedBlockingQueue<>(MAX_SIZE);  
11.     }  
12.   
13. /** 
14.      * 生产产品 
15.      *  
16.      * @param num 
17.      *            产品号码 
18.      */  
19. public void produce(int num) {  
20. // 阻塞队列会自动阻塞,不需要处理  
21. try {  
22.             q.put(num);  
23. "produce " + num);  
24. catch (InterruptedException e) {  
25.             e.printStackTrace();  
26.         }  
27.     }  
28.   
29. /** 
30.      * 消费产品 
31.      */  
32. public void comsume() {  
33. // 阻塞队列会自动阻塞,不需要处理  
34. try {  
35. "comsume " + q.take());  
36. catch (InterruptedException e) {  
37.             e.printStackTrace();  
38.         }  
39.     }  
40. }  
41.   
42. class Producer extends Thread {  
43. private MyQueue q;  
44.   
45. public Producer(MyQueue q) {  
46. this.q = q;  
47.     }  
48.   
49. @Override  
50. public void run() {  
51. for (int i = 0; i < 10; i++)  
52.             q.produce(i);  
53.     }  
54. }  
55.   
56. class Consumer extends Thread {  
57. private MyQueue q;  
58.   
59. public Consumer(MyQueue q) {  
60. this.q = q;  
61.     }  
62.   
63. @Override  
64. public void run() {  
65. for (int i = 0; i < 10; i++)  
66.             q.comsume();  
67.     }  
68. }  
69.   
70. public class Main {  
71. public static void main(String[] args) {  
72. new MyQueue();  
73. new Producer(q);  
74. new Consumer(q);  
75.         producer.start();  
76.         consumer.start();  
77.     }  
78. }


[java]  view plain  copy

 


1. import java.io.PipedInputStream;  
2. import java.io.PipedOutputStream;  
3.   
4. class MyQueue {  
5. int size; // 队列持有产品数(此例无用)  
6. final int MAX_SIZE = 5; // 队列最大容量  
7.     PipedInputStream pis;  
8.     PipedOutputStream pos;  
9.   
10. public MyQueue() {  
11. // 初始化流  
12. new PipedInputStream(MAX_SIZE);  
13. new PipedOutputStream();  
14. // 管道流建立连接  
15. try {  
16.             pos.connect(pis);  
17. catch (Exception e) {  
18.             e.printStackTrace();  
19.         }  
20.     }  
21.   
22. /** 
23.      * 生产产品 
24.      *  
25.      * @param num 
26.      *            产品号码 
27.      */  
28. public void produce(int num) {  
29. // 管道流会自动阻塞,不需要处理  
30. try {  
31. // 输出写在前面,否则会有奇怪的事情发生~  
32. "produce " + num);  
33.             pos.write(num);  
34.             pos.flush();  
35. catch (Exception e) {  
36.             e.printStackTrace();  
37.         }  
38.     }  
39.   
40. /** 
41.      * 消费产品 
42.      */  
43. public void comsume() {  
44. // 管道流会自动阻塞,不需要处理  
45. try {  
46. "comsume " + pis.read());  
47. catch (Exception e) {  
48.             e.printStackTrace();  
49.         }  
50.     }  
51.   
52. @Override  
53. protected void finalize() throws Throwable {  
54.         pis.close();  
55.         pos.close();  
56. super.finalize();  
57.     }  
58. }  
59.   
60. class Producer extends Thread {  
61. private MyQueue q;  
62.   
63. public Producer(MyQueue q) {  
64. this.q = q;  
65.     }  
66.   
67. @Override  
68. public void run() {  
69. for (int i = 0; i < 10; i++)  
70.             q.produce(i);  
71.     }  
72. }  
73.   
74. class Consumer extends Thread {  
75. private MyQueue q;  
76.   
77. public Consumer(MyQueue q) {  
78. this.q = q;  
79.     }  
80.   
81. @Override  
82. public void run() {  
83. for (int i = 0; i < 10; i++)  
84.             q.comsume();  
85.     }  
86. }  
87.   
88. public class Main {  
89. public static void main(String[] args) {  
90. new MyQueue();  
91. new Producer(q);  
92. new Consumer(q);  
93.         producer.start();  
94.         consumer.start();  
95.     }  
96. }





输出结果:

java 如何获得gc java获得class四种方法_java_03


 


25. ThreadLocal的设计理念与作用


ThreadLocal即线程本地存储。防止线程在共享资源上产生冲突的一种方式是根除对变量的共享。ThreadLocal是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储,ThreadLocal对象通常当做静态域存储,通过get和set方法来访问对象的内容:


[java]  view plain  copy

1. import java.util.Random;  
2. import java.util.concurrent.ExecutorService;  
3. import java.util.concurrent.Executors;  
4. import java.util.concurrent.TimeUnit;  
5.   
6. class Accessor implements Runnable {  
7. private final int id; // 线程id  
8. public Accessor(int id) {  
9. this.id = id;  
10.     }  
11. @Override  
12. public void run() {  
13. while(!Thread.currentThread().isInterrupted()) {  
14.             ThreadLocalVariableHolder.increment();  
15. this);  
16.             Thread.yield();  
17.         }  
18.     }  
19. @Override  
20. public String toString() {  
21. return "#" + id + " : " + ThreadLocalVariableHolder.get();  
22.     }  
23. }  
24. public class ThreadLocalVariableHolder {  
25. private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {  
26. // 返回随机数作为初始值  
27. protected Integer initialValue() {  
28. return new Random().nextInt(10000);  
29.         }  
30.     };  
31.       
32. /** 
33.      * 为当前线程的value值加一 
34.      */  
35. public static void increment() {  
36. 1);  
37.     }  
38.       
39. /** 
40.      * 返回当前线程存储的value值 
41.      * @return 
42.      */  
43. public static int get() {  
44. return value.get();  
45.     }  
46.       
47. public static void main(String[] args) throws InterruptedException {  
48.         ExecutorService service = Executors.newCachedThreadPool();  
49. // 开启5个线程  
50. for (int i = 0; i < 5; i++)  
51. new Accessor(i));  
52. // 所有线程运行3秒  
53. 1);  
54. // 关闭所有线程  
55.         service.shutdownNow();  
56.     }  
57. }


 


运行部分结果如下:


java 如何获得gc java获得class四种方法_java 如何获得gc_04


在上面的例子中虽然多个线程都去调用了ThreadLocalVariableHolder的increment和get方法,但这两个方法都没有进行同步处理,这是因为ThreadLocal保证我们使用的时候不会出现竞争条件。从结果来看,每个线程都在单独操作自己的变量,每个单独的线程都被分配了自己的存储(即便只有一个ThreadLocalVariableHolder对象),线程之间并没有互相造成影响。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。