Quantcast
Channel: CodeSection,代码区,Linux操作系统:Ubuntu_Centos_Debian - CodeSec
Viewing all articles
Browse latest Browse all 11063

Java多线程之多线程的锁机制

$
0
0

当两条线程同时访问一个类的时候,可能会带来一些问题。并发线程重入可能会带来内存泄漏、程序不可控等等。不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题。本篇总结主要著名Java的锁机制,阐述多线程下如何使用锁机制进行并发线程沟通。

1、并发下的程序异常

  先看下下面两个代码,查看异常内容。

  异常1:单例模式


Java多线程之多线程的锁机制
1 package com.scl.thread;
2
3 public class SingletonException
4 {
5 public static void main(String[] args)
6 {
7 // 开启十条线程进行分别测试输出类的hashCode,测试是否申请到同一个类
8 for (int i = 0; i < 10; i++)
9 {
10 new Thread(new Runnable()
11 {
12 @Override
13 public void run()
14 {
15 try
16 {
17 Thread.sleep(100);
18 }
19 catch (InterruptedException e)
20 {
21 e.printStackTrace();
22 }
23 System.out.println(Thread.currentThread().getName() + " " + MySingle.getInstance().hashCode());
24 }
25 }).start();
26 }
27 }
28 }
29
30 class MySingle
31 {
32 private static MySingle mySingle = null;
33
34 private MySingle()
35 {
36 }
37
38 public static MySingle getInstance()
39 {
40 if (mySingle == null) { mySingle = new MySingle(); }
41 return mySingle;
42 }
43 }
Java多线程之多线程的锁机制
view code

运行结果如下:


Java多线程之多线程的锁机制

  由上述可见,Thread-7与其他结果不一致,证明了在多线程并发的情况下这种单例写法存在问题,问题就在第40行。多个线程同时进入了空值判断,线程创建了新的类。

  异常2:线程重入,引发程序错误

现在想模拟国企生产规则,每个月生产100件产品,然后当月消费20件,依次更替。模拟该工厂全年的生产与销售

备注:举这个实例是为后面的信号量和生产者消费者问题做铺垫。可以另外举例,如开辟十条线程,每条线程内的任务就是进行1-10的累加,每条线程输出的结果不一定是55(线程重入导致)


Java多线程之多线程的锁机制
1 package com.scl.thread;
2
3 //每次生产100件产品,每次消费20件产品,生产消费更替12轮
4 public class ThreadCommunicateCopy
5 {
6 public static void main(String[] args)
7 {
8 final FactoryCopy factory = new FactoryCopy();
9 new Thread(new Runnable()
10 {
11
12 @Override
13 public void run()
14 {
15 try
16 {
17 Thread.sleep(2000);
18 }
19 catch (InterruptedException e)
20 {
21 e.printStackTrace();
22 }
23
24 for (int i = 1; i <= 12; i++)
25 {
26 factory.createProduct(i);
27 }
28
29 }
30 }).start();
31
32 new Thread(new Runnable()
33 {
34
35 @Override
36 public void run()
37 {
38 try
39 {
40 Thread.sleep(2000);
41 }
42 catch (InterruptedException e)
43 {
44 e.printStackTrace();
45 }
46
47 for (int i = 1; i <= 12; i++)
48 {
49 factory.sellProduct(i);
50 }
51
52 }
53 }).start();
54
55 }
56 }
57
58 class FactoryCopy
59 {
60 //生产产品
61 public void createProduct(int i)
62 {
63
64 for (int j = 1; j <= 100; j++)
65 {
66 System.out.println("第" + i + "轮生产,产出" + j + "件");
67 }
68 }
69 //销售产品
70 public void sellProduct(int i)
71 {
72 for (int j = 1; j <= 20; j++)
73 {
74 System.out.println("第" + i + "轮销售,销售" + j + "件");
75 }
76
77 }
78 }
View Code

结果如下:


Java多线程之多线程的锁机制

该结果不能把销售线程和生产线程的代码分隔开,如果需要分隔开。可以使用Java的锁机制。下面总结下如何处理以上两个问题。

2、使用多线程编程目的及一些Java多线程的基本知识

  使用多线程无非是期望程序能够更快地完成任务,这样并发编程就必须完成两件事情:线程同步及线程通信。

线程同步指的是:控制不同线程发生的先后顺序。

线程通信指的是:不同线程之间如何共享数据。

  Java线程的内存模型:每个线程拥有自己的栈,堆内存共享 [来源:Java并发编程艺术 ],如下图所示。 锁是线程间内存和信息沟通的载体,了解线程间通信会对线程锁有个比较深入的了解。后面也会详细总结Java是如何根据锁的信息进行两条线程之间的通信。

Java多线程之多线程的锁机制
2、使用Java的锁机制

Java语音设计和数据库一样,同样存在着代码锁.实现Java代码锁比较简单,一般使用两个关键字对代码进行线程锁定。最常用的就是volatile和synchronized两个

2.1 synchronized

synchronized关键字修饰的代码相当于数据库上的互斥锁。确保多个线程在同一时刻只能由一个线程处于方法或同步块中,确保线程对变量访问的可见和排它,获得锁的对象在代码结束后,会对锁进行释放。

synchronzied使用方法有两个:①加在方法上面锁定方法,②定义synchronized块。

模拟生产销售循环,可以通过synchronized关键字控制线程同步。代码如下:


Java多线程之多线程的锁机制
1 package com.scl.thread;
2
3 //每次生产100件产品,每次消费20件产品,生产消费更替10轮
4 public class ThreadCommunicate
5 {
6 public static void main(String[] args)
7 {
8 final FactoryCopy factory = new FactoryCopy();
9 new Thread(new Runnable()
10 {
11
12 @Override
13 public void run()
14 {
15 try
16 {
17 Thread.sleep(2000);
18 }
19 catch (InterruptedException e)
20 {
21 e.printStackTrace();
22 }
23
24 for (int i = 1; i <= 12; i++)
25 {
26 factory.createProduct(i);
27 }
28
29 }
30 }).start();
31
32 new Thread(new Runnable()
33 {
34
35 @Override
36 public void run()
37 {
38 try
39 {
40 Thread.sleep(2000);
41 }
42 catch (InterruptedException e)
43 {
44 e.printStackTrace();
45 }
46
47 for (int i = 1; i <= 12; i++)
48 {
49 factory.sellProduct(i);
50 }
51
52 }
53 }).start();
54
55 }
56 }
57
58 class Factory
59 {
60 private boolean isCreate = true;
61
62 public synchronized void createProduct(int i)
63 {
64 while (!isCreate)
65 {
66 try
67 {
68 this.wait();
69 }
70 catch (InterruptedException e)
71 {
72 e.printStackTrace();
73 }
74 }
75
76 for (int j = 1; j <= 100; j++)
77 {
78 System.out.println("第" + i + "轮生产,产出" + j + "件");
79 }
80 isCreate = false;
81 this.notify();
82 }
83
84 public synchronized void sellProduct(int i)
85 {
86 while (isCreate)
87 {
88 try
89 {
90 this.wait();
91 }
92 catch (InterruptedException e)
93 {
94 e.printStackTrace();
95 }
96 }
97 for (int j = 1; j <= 20; j++)
98 {
99 System.out.println("第" + i + "轮销售,销售" + j + "件");
100 }
101 isCreate = true;
102 this.notify();
103 }
104 }
View Code

  上述代码通过synchronized关键字控制生产及销售方法每次只能1条线程进入。代码中使用了isCreate标志位控制生产及销售的顺序。

备注:默认的使用synchronized修饰方法, 关键字会以当前实例对象作为锁对象,对线程进行锁定。

单例模式的修改可以在getInstance方式中添加synchronized关键字进行约束,即可。

wait方法和notify方法将在第三篇线程总结中讲解。

2.2 volatile

  volatile关键字主要用来修饰变量,关键字不像synchronized一样,能够块状地对代码进行锁定。该关键字可以看做对修饰的变量进行了读或写的同步操作。

  如以下代码:
Java多线程之多线程的锁机制

1 package com.scl.thread;
2
3 public class NumberRange
4 {
5 private volatile int unSafeNum;
6
7 public int getUnSafeNum()
8 {
9 return unSafeNum;
10 }
11
12 public void setUnSafeNum(int unSafeNum)
13 {
14 this.unSafeNum = unSafeNum;
15 }
16
17 public int addVersion()
18 {
19 return this.unSafeNum++;
20 }
21 }
View Code
代码编译后功能如下:
Java多线程之多线程的锁机制

1 package com.scl.thread;
2
3 public class NumberRange
4 {
5 private volatile int unSafeNum;
6
7 public synchronized int getUnSafeNum()
8 {
9 return unSafeNum;
10 }
11
12 public synchronized void setUnSafeNum(int unSafeNum)
13 {
14 this.unSafeNum = unSafeNum;
15 }
16
17 public int addVersion()
18 {
19 int temp = getUnSafeNum();
20 temp = temp + 1;
21 setUnSafeNum(temp);
22 return temp;
23 }
24
25 }
View Code

由此可见,使用volatile变量进行自增或自减操作的时候,变量进行temp= temp+1这一步时,多条线程同时可能同时操作这一句代码,导致内容出差。线程代码内的原子性被破坏了。

以上是对Java锁机制的总结,如有问题,烦请指出纠正。代码及例子很大一部分参考了《Java 并发编程艺术》[方腾飞 著] PDF+源码下载见 http://www.linuxidc.com/Linux/2016-07/133404.htm

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-07/133403.htm


Java多线程之多线程的锁机制

Viewing all articles
Browse latest Browse all 11063

Trending Articles