java并发-(2)同步机制

在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取。如果两个线程存取相同的对象,如果这两个线程都对这个对象进行了修改状态,可能会产生讹误的对象,这就是竞态条件。为了避免多线程引起的对共享数据的讹误,就需要通过各种手段来进行”同步“存取。

实现同步存取的方法

synchronize关键字

使用方式请参考上一篇文章

Lock接口的具体实现,如ReentrantLock、ReentrantReadWriteLock

一个未正确同步的例子
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
package com.luhc.concurrent.lock;

import java.util.concurrent.TimeUnit;

/**
* created by luhuancheng on 2018/10/9
*/
public class ReentrantLockDemo {

public static void main(String[] args) throws InterruptedException {
CountWorker worker = new CountWorker();
Thread t1 = new Thread(worker);
Thread t2 = new Thread(worker);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(worker.sum); // 按理说,两个线程运行任务的结果应该为200.但多次运行之后的实际结果却达不到预期
}

}

class CountWorker implements Runnable {

int sum;

@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 未进行同步的情况下,两个线程t1、t2同时对sum变量进行修改,可能会导致数据增加错误
sum++;

try {
// 为了提高多个线程同时修改变量的机会,此处睡眠一下
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
正确同步的例子
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
package com.luhc.concurrent.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* created by luhuancheng on 2018/10/9
*/
public class ReentrantLockDemo {

public static void main(String[] args) throws InterruptedException {
CountWorker worker = new CountWorker();
Thread t1 = new Thread(worker);
Thread t2 = new Thread(worker);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(worker.sum); // 按理说,两个线程运行任务的结果应该为200.但多次运行之后的实际结果却达不到预期
}

}

class CountWorker implements Runnable {

private Lock lock = new ReentrantLock();
int sum;

@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 对修改共享变量的逻辑快进行加锁
lock.lock();
try {
// 由于加锁的作用,确保了同一时刻只有一个线程对sum变量进行修改
sum++;
} finally {
// 在finally代码块进行锁的释放,确保锁一定会被释放
lock.unlock();
}

try {
// 为了提高多个线程同时修改变量的机会,此处睡眠一下
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}