锁如何使用?有什么注意事项?

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 方法,即可获得正确的值 30000for (int i = 0; i <1000; i++) {count++;}}//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000private 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 方法,即可获得正确的值 30000for (int i = 0; i <1000; i++) {count++;}}public static void main(String[] args) throws InterruptedException {//启动 30 个线程,每个线程 对 TestSynchronized 对象的 count 属性加 1000List<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 属性加 1000private 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 属性加 1000private 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 的 mapprivate 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 中的 keyRandom r = new Random();//生成随机数作为 valuefor (int i = 0; i <5; i++) {//5 个线程读 map 中 key 的 valuenew Thread(() -> {for (int j = 0; j <10; j++) {System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));}}).start();//5 个线程写 map 中 key 的 valuenew 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适合对共享资源写操作很少,读操作频繁的场景;可以从写锁降级到读锁,无法从读锁升级到写锁

给TA打赏
共{{data.count}}人
人已打赏
Java

什么是 happens-before 原则?

2020-7-31 0:40:00

Java

如何优雅地停止一个线程?

2020-7-31 0:43:20

本站所发布的一切源码、模板、应用等文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权。本站内容适用于DMCA政策。若您的权利被侵害,请与我们联系处理,站长 QQ: 84087680 或 点击右侧 私信:盾给网 反馈,我们将尽快处理。
⚠️
本站所发布的一切源码、模板、应用等文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权。本站内容适用于DMCA政策
若您的权利被侵害,请与我们联系处理,站长 QQ: 84087680 或 点击右侧 私信:盾给网 反馈,我们将尽快处理。
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索