线程之分析1:线程互斥

对线程做一个简单的分析。对以后的线程相关的都会有帮助,再者说线程也算是java的基本功了。就诸如武林秘籍中的易筋经!一定得学好,今天我就对线程做一个自己理解版的分析:

Q1:首先我们得明白什么是线程?

A1:首先我个人认为线程是一个程序的执行线索,小于一个进程,是内存、CPU占用的基本单位。

Q2:什么叫做线程互斥,他会导致什么后果?

A2:如果是这个问题,我会举个栗子(例子)来分析
ForExample:

银行转账的问题--->假如你的账户上余额为1000,有两个线程同时执行:
  • 1、有人往你账号上打了2000RMB:1000RMB+2000RMB;
  • 2、你刚好在淘宝上买了一件200RMB的帅气衣服:1000RMB-200RMB
  • 3、由于没有线程同步,可怕的事情发生了!在做完被转账的1000+2000,但没有被赋值时、另一个线程做完了1000-200的线程,并把800赋值了到了1000+2000的值上!
  • 4、啊啊啊,我的钱哪里去了!我的2800怎么变成了800!次奥啊!所以没有线程同步的你开始抓狂了!

这是一件多么可怕的事情啊!可见有些时候,线程互斥是多么的重要,只要是遇上两个线程会共享一个数据存储的时候,都要记得加上线程互斥!切记。(线程互斥就是指排他性,不允许多个线程同时访问一个共享资源,本质上来说线程互斥是一种特殊的线程同步,两者的概念不能混为一谈)

接下来让我们看看实例,一段java代码没有加互斥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init() {
final Outputer outputer = new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("zhushiqing");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("qianjiangqi");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("wangtianyi");
}
}
}).start();
}
static class Outputer {
public void output(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}

结果就是各种名字都不能好好输出!连名字都输不完整!我们能不能做朋友了!当然不能了!
所以,我们得给他们加上一个锁!synchronized关键字来修饰(可以修饰方法,也可以修饰方法里的代码块),而且是有锁当然得有钥匙是吧!这里的钥匙是指,被自己标志的一个上锁的对象(监视器),
比如说:
而且钥匙必须得是同一把,所以我们可以定义一把钥匙,如下

在代码块上加锁(监视器)的情况

1
2
3
4
5
6
7
8
9
10
11
12
static class Outputer {
public void output(String name) {
int len = name.length();
String xxx = "";
sychronized("xxx"){
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}

这样就给自己定义了一把钥匙(监视器),起多个这线程都不会再出现名字念不完整的情况了!我们又可以做朋友了!但是呢!我们这样做是多此一举的。一般来说,直接sychronized里面+(this)即可,直接代表了这个线程对象!也是唯一的!其实把到这样就差不多了!

还有就是直接给方法上加上synchronized关键字:

1
2
3
4
5
6
7
8
9
public synchronized void output2(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}

这样就不用再考虑对象的问题了,它默认都是用this

其实说到这里就差不多了,但是我觉得呢!还有一种情况:假如你用静态方法还行得通不?
代码如下,你觉得这样两个线程会好好运行吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public synchronized void output(String name) {
public void output(String name) {
int len = name.length();
sychronized(this){
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
public static synchronized void output3(String name) {
int len = name.length();
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}

答案是不互斥的!
为什么呢?因为static静态方法是随着类加载而加载的,所以他的对象锁是Outputer.class字节码,而output方法的对象所是this,不过只要把this换成Outputer.class两者就互斥了!

今天又对thread加深了了解!为自己赞一下!