1.Synchronized【隐式锁】
Java 内部使用monitor 也叫做监控锁或者内部锁来管理同步。这个monitor是绑定到对象上的。例如多线程调用同一个同步过的方法,这些方法的monitor都是同一个,而这个monitor是属于某个对象的。
所有隐式monitor都是想了可重入特性。可重入的意思是:锁是绑定到当前线程的,一个线程可以多次安全的获取到同一个锁而不会进入死锁。
private int count = 0;
// 线程不安全
private void increment() {
count = count + 1;
}
// 线程安全
private void incrementSync() {
synchronized (this) {
count = count + 1;
}
}
// 线程安全
private synchronized void incrementSync2() {
count = count + 1;
}
// 线程安全
private static void synchronizedTest() {
SynchronizedUtilTest test = new SynchronizedUtilTest();
ExecutorService executor = Executors.newFixedThreadPool(2);
IntStream.range(0, 10000).forEach(i -> executor.submit(test::incrementSync2));
stop(executor);
System.out.println(test.count);
}
2.ReentrantLock
重入锁
private static void reentrantLockTest() {
// tryLock()是lock()的一个替代方法,这个方法不阻塞当前线程,我们可以根据这个方法的执行结果来决定是否允许访问共享数据
SynchronizedUtilTest test = new SynchronizedUtilTest();
ExecutorService executor = Executors.newFixedThreadPool(2);
ReentrantLock lock = new ReentrantLock();
// 线程会持有锁1秒钟
executor.submit(() -> {
lock.lock();
try {
test.count++;
sleep(1);
} finally {
lock.unlock();
}
});
// 线程获取了当前锁的各种状态
executor.submit(() -> {
System.out.println("Locked: " + lock.isLocked());
System.out.println("Held by me: " + lock.isHeldByCurrentThread());
boolean locked = lock.tryLock();
System.out.println("Lock acquired: " + locked);
});
stop(executor);
System.out.println(test.count);
}
3.ReadWriteLock
这种锁维护了一对锁,读锁和写锁
private static void readWriteLockTest() {
// 读取任务必须等待写入任务完成才能开始;
// 一旦写入任务完成,读取任务开始后,两个读取任务是并行执行的,不用等待其中一个执行完成
SynchronizedUtilTest test = new SynchronizedUtilTest();
ExecutorService executor = Executors.newFixedThreadPool(2);
Map<String, String> map = new HashMap<>();
ReadWriteLock lock = new ReentrantReadWriteLock();
executor.submit(() -> {
lock.writeLock().lock(); // 获取写入锁
try {
sleep(1);
map.put("foo", "foo");
map.put("bar", "bar");
} finally {
lock.writeLock().unlock(); // 写入锁释放
}
});
Runnable readTask1 = () -> {
lock.readLock().lock(); // 获取读入锁
try {
System.out.println(map.get("foo"));
sleep(1);
} finally {
lock.readLock().unlock(); // 写入读释放
}
};
Runnable readTask2 = () -> {
lock.readLock().lock(); // 获取读入锁
try {
System.out.println(map.get("bar"));
sleep(1);
} finally {
lock.readLock().unlock(); // 写入读释放
}
};
executor.submit(readTask1);
executor.submit(readTask1);
executor.submit(readTask2);
executor.submit(readTask2);
stop(executor);
}
4.StampedLock
与ReadWriteLock相似
private static void stampedLockTest() {
// 与ReadWriteLock相似
ExecutorService executor = Executors.newFixedThreadPool(2);
Map<String, String> map = new HashMap<>();
StampedLock lock = new StampedLock();
executor.submit(() -> {
long stamp = lock.writeLock(); // 获取写锁,返回锁标识
try {
sleep(1);
map.put("foo", "foo");
map.put("bar", "bar");
} finally {
lock.unlockWrite(stamp); // 根据锁标识,释放写锁
}
});
Runnable readTask1 = () -> {
long stamp = lock.readLock(); // 获取读锁,返回锁标识
try {
System.out.println(map.get("foo"));
sleep(1);
} finally {
lock.unlockRead(stamp); // 根据锁标识,释放读锁
}
};
Runnable readTask2 = () -> {
long stamp = lock.readLock(); // 获取读锁,返回锁标识
try {
System.out.println(map.get("bar"));
sleep(1);
} finally {
lock.unlockRead(stamp); // 根据锁标识,释放读锁
}
};
executor.submit(readTask1);
executor.submit(readTask1);
executor.submit(readTask2);
executor.submit(readTask2);
stop(executor);
}
乐观锁
private static void stampedLock2Test() {
// 乐观锁
ExecutorService executor = Executors.newFixedThreadPool(2);
StampedLock lock = new StampedLock();
executor.submit(() -> {
long stamp = lock.tryOptimisticRead(); // 获取乐观锁, 返回锁标识;不会阻塞线程
try {
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp)); // 访问任何共享可变变量后验证这个锁是否还处于合法状态
sleep(1);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
sleep(2);
System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
} finally {
lock.unlock(stamp); // 根据锁标识,释放乐观锁
}
});
executor.submit(() -> {
long stamp = lock.writeLock(); // 获取写锁,返回锁标识
try {
System.out.println("Write Lock acquired");
sleep(2);
} finally {
lock.unlock(stamp); // 根据锁标识,释放写锁
System.out.println("Write done");
}
});
stop(executor);
}
读锁转换成写锁
private static void stampedLock3Test() {
// 读锁转换成写锁
SynchronizedUtilTest test = new SynchronizedUtilTest();
ExecutorService executor = Executors.newFixedThreadPool(2);
StampedLock lock = new StampedLock();
executor.submit(() -> {
long stamp = lock.readLock(); // 获取读锁, 返回锁标识
try {
if (test.count == 0) {
stamp = lock.tryConvertToWriteLock(stamp); // 读锁转换成写锁,不破坏线程安全性;不会阻塞线程
if (stamp == 0L) {
System.out.println("Could not convert to write lock");
stamp = lock.writeLock(); // 获取写锁, 返回锁标识
}
test.count = 23;
}
System.out.println(test.count);
} finally {
lock.unlock(stamp); // 根据锁标识,释放读锁
}
});
stop(executor);
}
5.Semaphore
计数信号量
锁通常用来保证变量或者资源的独占性,而信号量可以维护整个权限集合。
private static void semaphoresTest() {
// 信号量
ExecutorService executor = Executors.newFixedThreadPool(10); // 最大并发执行10个任务
Semaphore semaphore = new Semaphore(5); // 最大并发访问为5
Runnable longRunningTask = () -> {
boolean permit = false;
try {
permit = semaphore.tryAcquire(1, TimeUnit.SECONDS); // 尝试获取一个信号量
if (permit) {
System.out.println("Semaphore acquired");
sleep(5);
} else {
System.out.println("Could not acquire semaphore");
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} finally {
if (permit) {
semaphore.release(); // 释放一个信号量
}
}
};
IntStream.range(0, 10).forEach(i -> executor.submit(longRunningTask));
stop(executor);
}