Java多线程同步中常用synchronized、Lock等方法进行同步,但他们之间又有什么区别呢?
为什么要进行线程同步?
下面将用两个实例说明线程同步的作用。实例将演示100个线程分别累加20,如果计算正确,将输出结果2000.
不加锁
1 | public class SyncFunc implements Runnable { |
异常输出结果:1
1902
加锁
1 | public class SyncFunc implements Runnable { |
输出结果:1
2000
线程同步的作用
线程的同步是为了防止多个线程访问一个数据对象时,对数据造成破坏。
线程同步相关方法
将方法使用synchronized修饰,就成了同步方法,多个线程最多只能有一个线程能同时执行该方法,其他线程会调用此方法时处于阻塞状态。
有关synchronized的详细用法请参见:synchronized有趣的总结1
2
3public synchronized void run(){
}
synchronized还可以对某一个对象进行加锁,相比同步方法减少同步内容。多个线程在对某个变量加锁时,要保证该变量在所有线程中是同一个对象
。
有关synchronized的详细用法请参见:synchronized有趣的总结1
2synchronized(object){
}
volatile变量保证该变量对所有线程的可见性
,这里可见性是指当一条线程修改了这个变量的值,新值对其他线程是可以立即得知
的。详细介绍请参见:对Java中volatile关键词的思考
在JDK1.5之后,Java提供了另外一种线程同步机制:通过显示定义同步锁对象来实现同步,同步锁应该使用Lock对象充当。
ReentrantLock与synchronized基本对比
- 用法不同,Lock显示地指定起始位置和终止位置,比synchronized更精确。
- 性能不同,资源竞争不激烈时,synchronized>ReentrantLock;资源竞争激烈时,synchronized<ReentrantLock。
- 锁机制不同,synchronized获取多个锁时,释放顺序相反。Lock需要手动解锁,且最好在finally块中。
ReentrantLock基本用法: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
28public class ReenreantLockDemo implements Runnable {
public static int count = 0;
public final Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
ReenreantLockDemo reenreantLockDemo = new ReenreantLockDemo();
for (int i = 0; i < 20; i++) {
new Thread(reenreantLockDemo).start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(count);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
}
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。1
2
3
4
5
6
7private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
// TODO Auto-generated method stub
return 0;
}
};