Java 中常见的锁有

  • synchronized

  • 可重入锁 java.util.concurrent.lock.ReentrantLock

  • 可重复读写锁 java.util.concurrent.lock.ReentrantReadWriteLock

 

synchronized 有 3种用法

  • 修饰普通方法,执行方法代码,需要获取对象本身 this 的锁

package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
* 测试 synchronized 普通方法
* @author ConstXiong
* @date 2019-09-19 10:49:46
*/

public class TestSynchronizedNormalMethod {

private int count = 0;

//private void add1000() {
private synchronized void add1000() { //使用 synchronized 修饰 add100 方法,即可获得正确的值 30000
for (int i = 0; i < 1000; i++) {
count++;
}
}

//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
private void test() throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>(10);

for (int i = 0; i < 30; i++) {
Thread t =  new Thread(() -> {
add1000();
});
t.start();
threads.add(t);
}

//等待所有线程执行完毕
for (Thread t : threads) {
t.join();
}

//打印 count 的值
System.out.println(count);
}

public static void main(String[] args) throws InterruptedException {
//创建 TestSynchronizedNormalMethod 对象,调用 test 方法
new TestSynchronizedNormalMethod().test();
}
}

 

  • 修饰静态方法,执行方法代码,需要获取 class 对象的锁

package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
* 测试 synchronized 静态方法
* @author ConstXiong
* @date 2019-09-19 10:49:46
*/

public class TestSynchronizedStaticMethod {

private static int count = 0;

private static void add1000() {
//private synchronized static void add1000() { //使用 synchronized 修饰 add100 方法,即可获得正确的值 30000
for (int i = 0; i < 1000; i++) {
count++;
}
}

public static void main(String[] args) throws InterruptedException {

//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
List<Thread> threads = new ArrayList<Thread>(10);

for (int i = 0; i < 30; i++) {
Thread t =  new Thread(() -> {
add1000();
});
t.start();
threads.add(t);
}

//等待所有线程执行完毕
for (Thread t : threads) {
t.join();
}

//打印 count 的值
System.out.println(count);
}
}

 

  • 锁定 Java 对象,修饰代码块,显示指定需要获取的 Java 对象锁

package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
* 测试 synchronized 代码块
* @author ConstXiong
* @date 2019-09-19 10:49:46
*/

public class TestSynchronizedCodeBlock {

private int count = 0;

//锁定的对象
private final Object obj = new Object();

private void add1000() {

//执行下面的加 1000 的操作,都需要获取 obj 这个对象的锁
synchronized (obj) {
for (int i = 0; i < 1000; i++) {
count++;
}
}
}

//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
private void test() throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>(10);

for (int i = 0; i < 30; i++) {
Thread t =  new Thread(() -> {
add1000();
});
t.start();
threads.add(t);
}

//等待所有线程执行完毕
for (Thread t : threads) {
t.join();
}

//打印 count 的值
System.out.println(count);
}

public static void main(String[] args) throws InterruptedException {
//创建 TestSynchronizedNormalMethod 对象,调用 test 方法
new TestSynchronizedCodeBlock().test();
}
}

 

 

可重入锁 java.util.concurrent.lock.ReentrantLock 的使用示例

package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 测试 ReentrantLock
* @author ConstXiong
* @date 2019-09-19 11:26:50
*/

public class TestReentrantLock {

private int count = 0;

private final Lock lock = new ReentrantLock();

private void add1000() {
lock.lock();
try {
for (int i = 0; i < 1000; i++) {
count++;
}
} finally {
lock.unlock();
}
}

//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000
private void test() throws InterruptedException {
List<Thread> threads = new ArrayList<Thread>(10);

for (int i = 0; i < 30; i++) {
Thread t =  new Thread(() -> {
add1000();
});
t.start();
threads.add(t);
}

//等待所有线程执行完毕
for (Thread t : threads) {
t.join();
}

//打印 count 的值
System.out.println(count);
}

public static void main(String[] args) throws InterruptedException {
//创建 TestReentrantLock 对象,调用 test 方法
new TestReentrantLock().test();
}

}

 

 

可重复读写锁 java.util.concurrent.lock.ReentrantReadWriteLock 的使用示例

package constxiong.concurrency.a18;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* 测试可重入读写锁 ReentrantReadWriteLock
* @author ConstXiong
* @date 2019-09-19 11:36:19
*/

public class TestReentrantReadWriteLock {

//存储 key value 的 map
private Map<String, Object> map = new HashMap<String, Object>();

//读写锁
private final ReadWriteLock lock = new ReentrantReadWriteLock();

/**
* 根据 key 获取 value
* @param key
*/

public Object get(String key) {
Object value = null;
lock.readLock().lock();
try {
Thread.sleep(50L);
value = map.get(key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
return value;
}

/**
* 设置key-value
* @param key
*/

public void set(String key, Object value) {
lock.writeLock().lock();
try {
Thread.sleep(50L);
map.put(key, value);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}

//测试5个线程读数据,5个线程写数据
public static void main(String[] args) {
//创建测试可重入读写锁 TestReentrantReadWriteLock 对象
TestReentrantReadWriteLock test = new TestReentrantReadWriteLock();

String key = "lock";//存入 map 中的 key
Random r = new Random();//生成随机数作为 value

for (int i = 0; i < 5; i++) {
//5 个线程读 map 中 key 的 value
new Thread(() -> {
for (int j = 0; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));
}
}).start();

//5 个线程写 map 中 key 的 value
new Thread(() -> {
for (int j = 0; j < 10; j++) {
int value = r.nextInt(1000);
test.set(key, value);
System.out.println(Thread.currentThread().getName() + " write value=" + value);
}
}).start();
}
}

}

 

锁的使用注意事项

  • synchronized 修饰代码块时,最好不要锁定基本类型的包装类,如 jvm 会缓存 -128 ~ 127 Integer 对象,每次向如下方式定义 Integer 对象,会获得同一个 Integer,如果不同地方锁定,可能会导致诡异的性能问题或者死锁

Integer i = 100;
  • synchronized 修饰代码块时,要线程互斥地执行代码块,需要确保锁定的是同一个对象,这点往往在实际编程中会被忽视

  • synchronized  不支持尝试获取锁、锁超时和公平锁

  • ReentrantLock 一定要记得在 finally{} 语句块中调用 unlock() 方法释放锁,不然可能导致死锁

  • ReentrantLock 在并发量很高的情况,由于自旋很消耗 CPU 资源

  • ReentrantReadWriteLock 适合对共享资源写操作很少,读操作频繁的场景;可以从写锁降级到读锁,无法从读锁升级到写锁


更多相关文章

  1. 什么是线程池?
  2. 线程包括哪些状态?状态之间是如何转变的?
  3. 什么是守护线程?
  4. 如何优雅地停止一个线程?
  5. java多线程(11)AtomicBoolean原子类分析
  6. Java基础系列:线程同步和线程池
  7. java多线程(6)sleep和wait的4大区别
  8. java多线程(5)多线程通信出现的假死现象分析
  9. java多线程(4)使用wait/notify机制进行单线程之间的通信

随机推荐

  1. 使用php浏览文件时,编写文件的完整路径
  2. Windows 环境下php安装openssl证书
  3. CentOS下安装php 5.6.19
  4. php项目防止sql注入的方法
  5. ()子句中的MySQL查询在索引列上很慢
  6. Kohana和ORM的关系has_many_through
  7. 与vanilla JS的Ajax请求正在退回200OK
  8. 维基百科,如在PHP中列出标题的索引
  9. php 显示某一个时间相当于当前时间在多少
  10. php默认有最大执行时间