线程安全就是要保证数据的高度一致性和准确性,但不是一定要加锁才是线程安全性,只要代码里没有变量互串,线程之间互不影响,就是线程安全的。
要了解线程安全,可以先看一下线程不安全是怎样的一种现象。
public class TreadNoSafeDemo {
public static void main(String[] args) {
new ThreadNoSafeTest().start();
new ThreadNoSafeTest().start();
new ThreadNoSafeTest().start();
new ThreadNoSafeTest().start();
}
}
class ThreadNoSafeTest extends Thread{
private int num=5;
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"................."+(--num));
}
}
由运行结果可以看出:–num输出的并不是我们认为的 4 3 2 1;这就是线程的不安全现象。
那怎么才能实现线程安全呢?Java给我们提供了哪些机制?
想要实现线程安全大致有三种方法:
- 不适用单例模式,而是用多实例。
- 使用锁机制Synchronized、lock方式。
- 使用java.util.concurrent下面的类库。
下面对具体的方法进行介绍:
隐式锁(线程同步synchronized)
线程同步synchronized有两种方式,一种是放在同步方法上,一种是放在同步代码块里。
同步方法:
public synchronized void methodTest(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "................." + (--num));
}
同步代码块
public void methodTest(){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "................." + (--num));
}
}
显示锁Lock和ReentrantLock
加锁和解锁都是显示的。
Lock接口(java.util.concurrent.locks.Lock)的主要方法为:
ReentrantLock是Lock的实现类,是一个互斥的同步器。
在竞争条件下ReentrantLock的实现要比synchronized的实现更具有伸缩性,这意味着当许多线程都竞争相同锁定时,使用ReentrantLock的吞吐量通常要比synchronized好。
ReentrantLock是对方法块加锁使用频率最高点。
使用方法:
class ReentrantLockTest{
private final ReentrantLock reentrantLock=new ReentrantLock();
public void methodTest(){
reentrantLock.lock();
try {
//...............................
}catch (Exception e){
}
reentrantLock.unlock();
}
}
显示锁ReadWriteLock和ReentrantReadWriteLock
ReadWriteLock也是一个接口,提供了readLock和writeLock两种锁的操作机制,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。读写锁使用的场景是一个共享资源被大量的读取操作而只有少量的写操作。(读读不互斥,读写互斥,写写互斥;)。
ReentrantReadWriteLock和ReentrantLock区别:ReentrantReadWriteLock是对ReentrantLock的复杂扩展,ReentrantReadWriteLock可以实现一个方法中读写分离的锁的机制,而ReentrantLock加锁解锁只是一种机制。
显示锁StampedLock
StampedLock可大幅度提高程序的读取锁的吞吐量。在大量都是读取、很少写入的情况下,乐观读锁可以极大的提高吞吐量,也可以减少这种情况下写的饥饿现象。
查看:synchronized、ReentrantLock、ReentrantReadWriteLock 和StampedLock 的对比