锁,当多个事务同时对数据库表中的同一条数据操作时,如果没有加锁机制的话,就会产生脏数据(duty data)。
有2种机制可以解决这个问题:就是悲观锁和乐观锁了。
这里我们使用乐观锁,通过案例代码来实现一个乐观锁,达到解决问题的目的。
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
|
package
what21.thread.lock;
public
class
OptimLockMain {
// 文件版本号
static
int
version =
1
;
// 操作文件
/**
* 获取版本号
*
* @return
*/
public
static
int
getVersion(){
return
version;
}
/**
* 更新版本号
*/
public
static
void
updateVersion(){
version+=
1
;
}
/**
* @param args
*/
public
static
void
main(String[] args) {
for
(
int
i=
1
;i<=
5
;i++){
new
OptimThread(String.valueOf(i),getVersion(),file).start();
}
}
}
|
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
package
what21.thread.lock;
import
java.io.BufferedReader;
import
java.io.File;
import
java.io.FileReader;
import
java.io.FileWriter;
import
java.io.IOException;
public
class
OptimThread
extends
Thread {
// 文件版本号
public
int
version;
// 文件
public
String file;
public
OptimThread(String name,
int
version,String file){
this
.setName(name);
this
.version = version;
this
.file = file;
}
public
void
run() {
// 1. 读取文件
String text = read(file);
println(
"线程"
+ getName() +
",文件版本号为:"
+ OptimLockMain.getVersion());
println(
"线程"
+ getName() +
",版本号为:"
+ getVersion());
// 2. 写入文件
if
(OptimLockMain.getVersion() == getVersion()){
println(
"线程"
+ getName() +
",版本号为:"
+ version +
",正在执行"
);
// 文件操作,这里用synchronized就相当于文件锁
// 如果是数据库,相当于表锁或者行锁
synchronized
(OptimThread.
class
){
if
(OptimLockMain.getVersion() ==
this
.version){
// 写入操作
write(file, text);
// 更新文件版本号
OptimLockMain.updateVersion();
return
;
}
}
}
// 3. 版本号不正确的线程,需要重新读取,重新执行
println(
"线程"
+ getName() +
",文件版本号为:"
+ OptimLockMain.getVersion());
println(
"线程"
+ getName() +
",版本号为:"
+ getVersion());
System.err.println(
"线程"
+ getName() +
",需要重新执行。"
);
}
/**
* @return
*/
private
int
getVersion(){
return
this
.version;
}
/**
* 写入数据
*
* @param file
* @param text
*/
public
static
void
write(String file,String text){
try
{
FileWriter fw =
new
FileWriter(file,
false
);
fw.write(text +
"\r\n"
);
fw.flush();
fw.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
/**
* 读取数据
*
* @param file
* @return
*/
public
static
String read(String file){
StringBuilder sb =
new
StringBuilder();
try
{
File rFile =
new
File(file);
if
(!rFile.exists()){
rFile.createNewFile();
}
FileReader fr =
new
FileReader(rFile);
BufferedReader br =
new
BufferedReader(fr);
String r =
null
;
while
((r=br.readLine())!=
null
){
sb.append(r).append(
"\r\n"
);
}
br.close();
fr.close();
}
catch
(IOException e) {
e.printStackTrace();
}
return
sb.toString();
}
/**
* @param content
*/
public
static
void
println(String content){
System.out.println(content);
}
}
|
1、这里线程对文件写入的操作,就相当于修改表中的一条数据。
2、多个线程需要对这个文件写入时,同时只能有一个线程写入该文件。
3、同时只能有一个线程写入文件,就相当于与表的行锁,同一时间只允许一个线程操作。
4、线程初始化时,持有相同的版本号,写入文件操作完成后,然后变更版本号。
5、其他线程再次操作时,比较版本号,版本号较低的不在进行写入操作,或者抛出异常,或者再次执行该操作,这样就防止产生脏数据了。