Java多线程的实现方式二
Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和
内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import java.util.concurrent.locks.ReentrantLock; class Window implements Runnable{ private int tic = 10 ; //实例化lock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while ( true ){ try { //调用lock锁定方法 lock.lock(); if (tic> 0 ){ try { Thread.sleep( 100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ ":飘号" + ":" +tic); tic--; } else { break ; } } finally { //3.调用解锁方法 lock.unlock(); } } } } public class LookTest { public static void main(String[] args) { Window w = new Window(); Thread t1= new Thread(w); Thread t2= new Thread(w); Thread t3= new Thread(w); t1.start(); t2.start(); t3.start(); } } 测试结果 Thread- 0 :飘号: 10 Thread- 0 :飘号: 9 Thread- 0 :飘号: 8 Thread- 0 :飘号: 7 Thread- 1 :飘号: 6 Thread- 1 :飘号: 5 Thread- 1 :飘号: 4 Thread- 1 :飘号: 3 Thread- 2 :飘号: 2 Thread- 2 :飘号: 1 Process finished with exit code 0 |
synchronized 与lock的
形同点解决线程安全问题
不同点:synchronized机制在执行完相应的代码块以后,自动的释放同步监视器
lock需要手动的启动同步,同时结束也需要手动释放监视器
线程通信问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | /** * 线程通信的例子 * wait():一旦线程执行此方法进入阻塞状态,并且释放锁 * notify(): 一旦执行此方法,就会和唤醒wait的线程,如果多个线程wait,就唤醒优先及高的线程 * notifyAll():一旦执行此方法,就会唤醒所以被wait的线程 *说明:1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块,或同步方法中。 * 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器; * 否则java.lang.IllegalMonitorStateException * */ class Number implements Runnable{ private int number=1; private Object obj = new Object(); @Override public void run() { while ( true ){ synchronized (obj) { obj.notify(); if (number<=10){ System.out.println(Thread.currentThread().getName()+ ":" +number); number++; try { // 使得调用此wait()方法的线程进入阻塞状态,并会释放锁 obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { break ; } } } } } public class CommunicationTest { public static void main(String[] args) { Number n1 = new Number(); Thread thread1 = new Thread(n1); Thread thread2 = new Thread(n1); thread1.setName( "线程1" ); thread2.setName( "线程2" ); thread1.start(); thread2.start(); // System.out.println(); } } 测试结果 线程1:1 线程2:2 线程1:3 线程2:4 线程1:5 线程2:6 线程1:7 线程2:8 线程1:9 线程2:10 Process finished with exit code 0 |
sleep()和wait()方法
相同点:线程执行到它们的时候进入阻塞状态
不同点:两个方法声明的位置不同;Thread类中声明seelp(),Object中声明的wait()
调用的范围不一样,seelp()可以在任何需要的场景下调用,wait()必须使用在同步代码块
是否释放同步监视器的问题:如果两个方法都使用在同步代码块或者同步方法中,seelp()方法不会释放锁,而wait()会释放锁
线程通信应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | class Clerk{ private int productCount = 0 ; public void producProuluct() { if (productCount< 20 ){ productCount++; System.out.println(Thread.currentThread().getName()+ ":开始生产" +productCount); } } //消费 public void consumeProduct(){ if (productCount> 0 ){ System.out.println(Thread.currentThread().getName()+ ":" +productCount); productCount--; } } } class Producer extends Thread{ //生产者 private Clerk clerk; public Producer(Clerk clerk){ this .clerk= clerk; } @Override public void run() { System.out.println(getName()+ ":生成" ); while ( true ){ clerk.producProuluct(); } } } class Consumer extends Thread{ private Clerk clerk; public Consumer(Clerk clerk){ this .clerk= clerk; } @Override public void run() { System.out.println(getName()+ ":消费" ); while ( true ){ clerk.consumeProduct(); } } } public class ProductTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer p1 = new Producer(clerk); p1.setName( "生产者1" ); Consumer consumer = new Consumer(clerk); consumer.setName( "消费者" ); p1.start(); consumer.start(); } } 测试结果 生产者 1 :开始生产 3 生产者 1 :开始生产 1 生产者 1 :开始生产 2 生产者 1 :开始生产 3 生产者 1 :开始生产 4 生产者 1 :开始生产 5 生产者 1 :开始生产 6 生产者 1 :开始生产 7 生产者 1 :开始生产 8 消费者: 1 消费者: 8 消费者: 7 消费者: 6 消费者: 5 消费者: 4 生产者 1 :开始生产 9 生产者 1 :开始生产 4 生产者 1 :开始生产 5 生产者 1 :开始生产 6 生产者 1 :开始生产 7 生产者 1 :开始生产 8 生产者 1 :开始生产 9 生产者 1 :开始生产 10 生产者 1 :开始生产 11 生产者 1 :开始生产 12 生产者 1 :开始生产 13 消费者: 3 消费者: 13 生产者 1 :开始生产 14 生产者 1 :开始生产 14 生产者 1 :开始生产 15 生产者 1 :开始生产 16 生产者 1 :开始生产 16 生产者 1 :开始生产 17 生产者 1 :开始生产 18 生产者 1 :开始生产 19 生产者 1 :开始生产 20 消费者: 15 消费者: 20 消费者: 19 消费者: 18 消费者: 17 |
草都可以从石头缝隙中长出来更可况你呢
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
2020-04-21 kubernetes 之ingress-nginx对象实现代理后端主机并实现ssl回话卸载