Java多线程之Lock的使用

简介: import java.util.concurrent.ExecutorService;  import java.util.concurrent.
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3. import java.util.concurrent.Future;  
  4. import java.util.concurrent.locks.Lock;  
  5. import java.util.concurrent.locks.ReadWriteLock;  
  6. import java.util.concurrent.locks.ReentrantLock;  
  7. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  8.   
  9. /** 
  10.  * Lockers 
  11.  * 在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性, 
  12.  * 在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作, 
  13.  * 从而保证数据的完整性。在5.0以前,锁定的功能是由Synchronized关键字来实现的。 
  14.  */  
  15. public class Lockers {  
  16.       
  17.     /** 
  18.      * 测试Lock的使用。在方法中使用Lock,可以避免使用Synchronized关键字。 
  19.      */  
  20.     public static class LockTest {  
  21.   
  22.         Lock lock = new ReentrantLock();// 锁  
  23.         double value = 0d; // 值  
  24.         int addtimes = 0;  
  25.   
  26.         /** 
  27.          * 增加value的值,该方法的操作分为2步,而且相互依赖,必须实现在一个事务中 
  28.          * 所以该方法必须同步,以前的做法是在方法声明中使用Synchronized关键字。 
  29.          */  
  30.         public void addValue(double v) {  
  31.             lock.lock();// 取得锁  
  32.             System.out.println("LockTest to addValue: " + v + "   "  
  33.                     + System.currentTimeMillis());  
  34.             try {  
  35.                 Thread.sleep(1000);  
  36.             } catch (InterruptedException e) {  
  37.             }  
  38.             this.value += v;  
  39.             this.addtimes++;  
  40.             lock.unlock();// 释放锁  
  41.         }  
  42.   
  43.         public double getValue() {  
  44.             return this.value;  
  45.         }  
  46.     }  
  47.     public static void testLockTest() throws Exception{  
  48.         final LockTest lockTest = new LockTest();  
  49.         // 新建任务1,调用lockTest的addValue方法  
  50.         Runnable task1 = new Runnable(){  
  51.             public void run(){  
  52.                 lockTest.addValue(55.55);  
  53.             }  
  54.         };  
  55.         // 新建任务2,调用lockTest的getValue方法  
  56.         Runnable task2 = new Runnable(){  
  57.             public void run(){  
  58.                 System.out.println("value: " + lockTest.getValue());  
  59.             }  
  60.         };  
  61.         // 新建任务执行服务  
  62.         ExecutorService cachedService = Executors.newCachedThreadPool();  
  63.         Future future = null;  
  64.         // 同时执行任务1三次,由于addValue方法使用了锁机制,所以,实质上会顺序执行  
  65.         for (int i=0; i<3; i++){  
  66.             future = cachedService.submit(task1);  
  67.         }  
  68.         // 等待最后一个任务1被执行完  
  69.         future.get();  
  70.         // 再执行任务2,输出结果  
  71.         future = cachedService.submit(task2);  
  72.         // 等待任务2执行完后,关闭任务执行服务  
  73.         future.get();  
  74.         cachedService.shutdownNow();  
  75.     }  
  76.       
  77.     /** 
  78.      * ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。 
  79.      * 多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock, 
  80.      * 而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有: 
  81.      * readLock(): 返回一个读的lock  
  82.      * writeLock(): 返回一个写的lock, 此lock是排他的。 
  83.      * ReadWriteLockTest很适合处理类似文件的读写操作。 
  84.      * 读的时候可以同时读,但不能写;写的时候既不能同时写也不能读。 
  85.      */  
  86.     public static class ReadWriteLockTest{  
  87.         // 锁  
  88.         ReadWriteLock lock = new ReentrantReadWriteLock();  
  89.         // 值  
  90.         double value = 0d;  
  91.         int addtimes = 0;  
  92.           
  93.         /** 
  94.          * 增加value的值,不允许多个线程同时进入该方法 
  95.          */  
  96.         public void addValue(double v) {  
  97.             // 得到writeLock并锁定  
  98.             Lock writeLock = lock.writeLock();  
  99.             writeLock.lock();  
  100.             System.out.println("ReadWriteLockTest to addValue: " + v + "   "  
  101.                     + System.currentTimeMillis());  
  102.             try {  
  103.                 Thread.sleep(1000);  
  104.             } catch (InterruptedException e) {  
  105.             }  
  106.             try {  
  107.                 // 做写的工作  
  108.                 this.value += v;  
  109.                 this.addtimes++;  
  110.             } finally {  
  111.                 // 释放writeLock锁  
  112.                 writeLock.unlock();  
  113.             }  
  114.         }  
  115.         /** 
  116.          * 获得信息。当有线程在调用addValue方法时,getInfo得到的信息可能是不正确的。 
  117.          * 所以,也必须保证该方法在被调用时,没有方法在调用addValue方法。 
  118.          */  
  119.         public String getInfo() {  
  120.             // 得到readLock并锁定  
  121.             Lock readLock = lock.readLock();  
  122.             readLock.lock();  
  123.             System.out.println("ReadWriteLockTest to getInfo   "  
  124.                     + System.currentTimeMillis());  
  125.             try {  
  126.                 Thread.sleep(1000);  
  127.             } catch (InterruptedException e) {  
  128.             }  
  129.             try {  
  130.                 // 做读的工作  
  131.                 return this.value + " : " + this.addtimes;  
  132.             } finally {  
  133.                 // 释放readLock  
  134.                 readLock.unlock();  
  135.             }  
  136.         }  
  137.     }  
  138.       
  139.     public static void testReadWriteLockTest() throws Exception{  
  140.         final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();  
  141.         // 新建任务1,调用lockTest的addValue方法  
  142.         Runnable task_1 = new Runnable(){  
  143.             public void run(){  
  144.                 readWriteLockTest.addValue(55.55);  
  145.             }  
  146.         };  
  147.         // 新建任务2,调用lockTest的getValue方法  
  148.         Runnable task_2 = new Runnable(){  
  149.             public void run(){  
  150.                 System.out.println("info: " + readWriteLockTest.getInfo());  
  151.             }  
  152.         };  
  153.         // 新建任务执行服务  
  154.         ExecutorService cachedService_1 = Executors.newCachedThreadPool();  
  155.         Future future_1 = null;  
  156.         // 同时执行5个任务,其中前2个任务是task_1,后两个任务是task_2  
  157.         for (int i=0; i<2; i++){  
  158.             future_1 = cachedService_1.submit(task_1);  
  159.         }  
  160.         for (int i=0; i<2; i++){  
  161.             future_1 = cachedService_1.submit(task_2);  
  162.         }  
  163.         // 最后一个任务是task_1  
  164.         future_1 = cachedService_1.submit(task_1);  
  165.         // 这5个任务的执行顺序应该是:  
  166.         // 第一个task_1先执行,第二个task_1再执行;这是因为不能同时写,所以必须等。  
  167.         // 然后2个task_2同时执行;这是因为在写的时候,就不能读,所以都等待写结束,  
  168.         // 又因为可以同时读,所以它们同时执行  
  169.         // 最后一个task_1再执行。这是因为在读的时候,也不能写,所以必须等待读结束后,才能写。  
  170.           
  171.         // 等待最后一个task_2被执行完  
  172.         future_1.get();  
  173.         cachedService_1.shutdownNow();  
  174.     }  
  175.   
  176.     public static void main(String[] args) throws Exception{  
  177.         Lockers.testLockTest();  
  178.         System.out.println("---------------------");  
  179.         Lockers.testReadWriteLockTest();  
  180.     }  
目录
相关文章
|
17天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
142 60
【Java并发】【线程池】带你从0-1入门线程池
|
6天前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
58 23
|
13天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
81 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
1月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
109 14
|
1月前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
57 13
|
1月前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
2月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
128 17
|
3月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
2月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
10月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。

热门文章

最新文章