Open MoJieBlog opened 5 years ago
4种Java线程锁(线程同步)
比如:
- synchronized
- ReentrantLock
- ReentrantReadWriteLock
- AtomicInteger
当然也可以是其他锁
还有一个 unsafe 类;
cas 是乐观锁的一种实现形式, 在多核 CPU 机器上会有比较好的性能;
首先,Java为什么会出现锁的概念,锁是用来干什么的?这里我给大家解释一下,锁 是保证 线程同步与安全的.
synchronized 有什么用?大家看一下这段代码
public class Synchronized2Demo implements TestDemo {
private int x = 0;
private void count() {
x++;
}
@Override
public void runTest() {
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 1: " + x);
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 1_000_000; i++) {
count();
}
System.out.println("final x from 2: " + x);
}
}.start();
}
}
当添加 synchronized 的时候,多个线程不能同时访问同一个方法,synchronized 的意义是保证方法或代码块的内部资源的互斥访问,同一时间用用一个monitor监视代码最多一个代码访问,怎么解释这句话呢?
运行上段代码,我们不难发现:
final x from 1: 1000000
final x from 2: 1314570
Fuck! 竟然结果不是200 0000,而且每次结果都不一样,这是为什么呢?
因为:x++ 不是原子操作, 不是一行代码执行的,他是可以分解的代码,可以分离成这样
int temp = i;
i + 1 = temp;
你看,这是两段代码吧,加 synchronized 修饰 就可以完美解决这个问题.
保证方法的内部或内部代码块内部资源(数据)的互斥访问,即同一时间,由同一个 Monitor 监视代码,最多只能一个线程在访问
保证线程之间对监视资源的数据同步.即,任何线程获取 Monitor 后第一时间,会将共享数据的缓存复制到自己的缓存中;任何线程在释放 Monitor 的第一时间,会将缓存的数据复制到共享内存中
同样是加锁机制 ,使用方式更加灵活,同时也更加麻烦一些
Lock lock = new ReentrantLock();
{
// -------伪代码-------
}
lock.lock(); try {
x++;
} finally {
lock.unlock();
}
finally 作用是保证在方法提前结束或出现Exception的时候,依然能正常释放锁.
一般不会直接使用 Lock,而是直接使用更加复杂的锁,如: ReadWriteLock:
public class ReadWriteLockDemo implements TestDemo {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
private int x = 0;
@Override
public void runTest() {
print(15);
count();
}
public void count() {
writeLock.lock();
try {
x++;
} finally {
writeLock.unlock();
}
}
public void print(int time) {
readLock.lock();
try {
for (int i = 0; i < time; i++) {
System.out.println(x + "");
}
System.out.println("over");
} finally {
readLock.unlock();
}
}
}
AtomicInteger atomicInteger = new AtomicInteger(0);
{
// -------伪代码--------
}
atomicInteger.getAndIncrement();
解决了 ++ 原子性 问题,非常好用.
不管数据怎么样我都会加锁,这个就是悲观锁
我认为,别人不会读数据我倾向于别人不会动数据,所以不对代码块加锁,这就是乐观锁
在多线程访问共同资源时,在某一个线程对资源进行写操作中途(写入已经开始了,但还没结束),其他线程对这个写了一半资源进行读操作,或基于这个写了一半的资源进行了写操作,导致出现数据错误
通过共享资源进行访问限制,让同一时间只有一个线程可以访问资源,保证数据的准确性
无论是线程安全问题,还是针对线程衍生的锁机制,它的核心在于共享资源,而不是某个方法或几行代码
当然还有死锁问题,这里我就不一一和大家阐述了.
最后大家觉的我的内容对大家有帮助,欢迎通过以下渠道阅读我的文章或github star +1
github | CSDN | 掘金 |
---|---|---|
MicroKibaco | Kibaco | 小木箱 |
比如:
当然也可以是其他锁