先看一下使用Lock的例子(javaDoc的例子):
其实看下java.util.concurrent.ArrayBlockingQueue的代码,就会发现,下面的put/take方法其实就是java.util.concurrent.ArrayBlockingQueue的put/take方法的实现。
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class BoundedBuffer { final Lock lock = new ReentrantLock(); //一个Lock可以对应多个Condition,但一个Condition只能对应一个Lock //Condition对象可以细化等待(Condition.await()/Object.wait())和通知(Condition.signalAll()/Object.notifyAll())的粒度。 //例如这里的notFull、notEmpty,当调用Condition.await()之后,只有接收到对应的Condition的signalAll()才能继续。这一点是synchronized不能实现的 final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[2]; int putIndex, takeIndex, count; public void put(Object x) throws InterruptedException { lock.lock();//必须手动获取锁 try { while (count == items.length){ notFull.await();//等待在notFull这个Condition的通知 } items[putIndex] = x; if (++putIndex == items.length){ putIndex = 0; } ++count; notEmpty.signalAll();//通知等待在notEmpty这个Condition上面的线程 } finally { lock.unlock();//必须手动释放锁 } } public Object take() throws InterruptedException { lock.lock();//必须手动获取锁 try { while (count == 0){ notEmpty.await();//等待在notEmpty这个Condition的通知 } Object x = items[takeIndex]; if (++takeIndex == items.length){ takeIndex = 0; } --count; notFull.signalAll();//通知等待在notFull这个Condition上面的线程 return x; } finally { lock.unlock();//必须手动释放锁 } } }
然后是使用synchronized关键字的例子:
class BoundedBuffer2 { final Object[] items = new Object[2]; int putIndex, takeIndex, count; /** * 锁定对象(每个对象都有一个对应的锁) */ private static final Object monitor = new Object(); public void put(Object x) throws InterruptedException { //synchronized(monitor)这句话就包含了获取monitor的锁,相当于monitor对应的lock的lock.lock() //并且synchronized会在锁定的方法或者代码块结束之后,自动释放对象的锁(lock.unlock()) synchronized(monitor){ while (count == items.length){ monitor.wait(); } items[putIndex] = x; if (++putIndex == items.length){ putIndex = 0; } ++count; monitor.notifyAll();//对应上面例子中的signalAll } } public Object take() throws InterruptedException { Object x = null; //synchronized(monitor)这句话就包含了获取monitor的锁,相当于monitor对应的lock的lock.lock() //并且synchronized会在锁定的方法或者代码块结束之后,自动释放对象的锁(lock.unlock()) synchronized(monitor){ while (count == 0){ monitor.wait(); } x = items[takeIndex]; if (++takeIndex == items.length){ takeIndex = 0; } --count; monitor.notifyAll();//对应上面例子中的signalAll } return x; } }
测试:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Test { public static void main(String[] args) throws Exception { m(); } static ExecutorService e =Executors.newFixedThreadPool(5); static BoundedBuffer2 b2 = new BoundedBuffer2(); static BounderBuffer b = new BounderBuffer(); static void m(){ int aaa=3; while(aaa-->0){ remove(); add(aaa); } e.shutdown(); } static void remove(){ e.execute(new Runnable(){ public void run(){ try { System.out.println("take : "+b.take()); System.out.println("take : "+b2.take()); } catch (InterruptedException e) { e.printStackTrace(); } } }); } static void add(final int i){ e.execute(new Runnable(){ public void run(){ try { System.out.println("add : "+i); b.put(i); //b2.put(i); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
从结果看,两个例子功能是一样的。
不同的是:Lock的方式必须手动的获取/释放锁,而使用synchronized的方式则不需要,因为它自动包含了获取/释放锁的过程(这也是为什么推荐使用后者,因为它简单,易操作,但是对于需要细粒度的控制同步操作的时候,还是要使用前者)
另外,使用Lock的时候,等待/通知 是使用的Condition对象的await()/signal()/signalAll() ,而使用synchronized的时候,则是对象的wait()/notify()/notifyAll();由此可以看出,使用Lock的时候,粒度更细了,一个Lock可以对应多个Condition,
当需要等待某个条件满足的时候,就使用该条件的Condition的await(),等待其它线程调用该Condition的signal()/signalAll()
个人的初步理解。
下面是一个问题和答案,同时也解决了自己的疑惑:
下面的代码是Condition类的javadoc中的一个示例
:
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
假设有两个线程:生产者、消费者,分别调用某个BoundedBuffer对象
的put()和take()方法
如果消费者先执行,调用了该对象的take()方法,并且运行到了while循环处的notEmpty.await();
那么此时,消费者是已经得到了lock的,那为什么生产者却可以进到put()方法的内部,并且获得lock呢?
原因:
Lock
and synchronized
在等待的时候,都会释放对象的锁的。
例如:Object.wait()就会释放synchronized的锁,Condition.await()会释放对应的Lock的锁。
Both Lock
and synchronized
temporarily allow others to obtain the lock when they are waiting. To stop waiting, a thread have to re-acquire the lock.
Note: They don't release it fully and if you take a stack trace you can have multiple threads which appear to be holding the lock at once, but at most one of them will be running (the rest will be waiting)
From Condition.await()
The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:
- Some other thread invokes the signal() method for this Condition and the current thread happens to be chosen as the thread to be awakened; or
- Some other thread invokes the signalAll() method for this Condition; or
- Some other thread interrupts the current thread, and interruption of thread suspension is supported; or
- A "spurious wakeup" occurs.
In all cases, before this method can return the current thread must re-acquire the lock associated with this condition. When the thread returns it is guaranteed to hold this loc
相关推荐
AQS通过内部类Sync映射所有同步器调用,维护资源状态的可用性最后,文档提供了AQS源码的初步分析,突出了其设计和实现的关键部分,如等待队列节点类Node的定义综合来看,文章为Java开发者提供了对AQS及其在...
- 线程同步机制包括synchronized关键字、wait(), notify(), notifyAll()方法以及Lock接口。 8. **网络编程**: - Java提供了Socket和ServerSocket类进行TCP编程,DatagramSocket和DatagramPacket类用于UDP编程。 ...
4. **线程同步机制**:如`synchronized`关键字或`Lock`对象,保证多线程环境下的数据一致性,防止并发问题。 5. **异常处理**:确保即使在出现异常的情况下,时钟仍然能够正常运行,避免程序崩溃。 从代码实现的...
- Synchronized和Lock的区别和用法:理解同步关键字synchronized和锁接口Lock的不同用法和优势。 - Final、Finally、Finalize的区别:掌握这三个关键字的用途和区别。 - OverLoad与Override的区别:明确方法重载...
在Java领域,并发编程是构建高性能、高效率系统的关键技术,对于软件开发人员来说,理解和掌握这部分知识至关重要。 第一章可能涵盖了并发编程的基本概念,包括线程、进程、同步与异步等核心概念。线程是操作系统...
线程同步是避免数据竞争的关键,包括synchronized关键字、wait()、notify()和notifyAll()方法,以及Lock接口和相关的并发工具类。 6. **集合框架**: 集合框架是存储和操作对象的重要工具,包括List(如ArrayList...
8. **多线程**:掌握线程的创建方式(继承Thread类、实现Runnable接口),了解同步机制(synchronized关键字、wait/notify、Lock接口等)以及并发工具类(如ExecutorService、Semaphore、CountDownLatch)。...
- **同步机制**:掌握synchronized关键字,wait()、notify()、notifyAll()方法的使用,以及Lock接口和ReentrantLock类。 - **线程池**:理解ExecutorService、ThreadPoolExecutor和ScheduledExecutorService的作用...
7. **多线程**:学习并发编程,包括线程的创建、同步机制(如synchronized关键字和Lock接口)以及并发工具类。 8. **反射**:理解反射机制,如何在运行时动态获取类信息并操作对象。 9. **注解(Annotation)**:...
9. **线程编程**:学习多线程的概念,创建和管理线程,以及同步机制,如synchronized关键字和Lock接口。 10. **Java反射机制**:探索Java反射API,允许程序在运行时动态获取类的信息并进行操作。 11. **枚举与注解...
5. **多线程**:线程的创建(Thread类和Runnable接口)、线程同步(synchronized关键字,wait()、notify()方法,Lock接口),线程池的使用(ExecutorService)。 6. **反射机制**:如何在运行时动态获取类的信息,...
7. **多线程**:学习线程的基本概念,如何创建和管理线程,以及同步机制,如synchronized关键字和Lock接口。 8. **反射机制**:了解如何在运行时动态地获取类的信息,并调用方法或访问字段。 9. **枚举与注解**:...
理解线程同步的概念,包括synchronized关键字、wait()、notify()和notifyAll()方法,以及如何使用Lock接口和Condition,可以帮助编写高效的并发程序。 5. **输入/输出流**:Java的I/O流系统强大而灵活,涵盖了文件...
6. **多线程**:线程的创建方式、同步机制(synchronized,Lock等)、并发工具类(如Semaphore、CountDownLatch等)。 7. **IO流**:文件操作,字节流与字符流,缓冲流,以及NIO(New IO)的理解。 8. **网络编程*...
10. **多线程**:学习如何创建和管理线程,包括同步机制(synchronized关键字、wait/notify、Lock接口等)和并发工具类。 11. **Java Swing**:初步接触GUI编程,使用Swing库创建简单的桌面应用程序,如按钮、...
3. **多线程**:掌握Thread类和Runnable接口,实现并发执行,以及同步机制(synchronized关键字、wait/notify、Lock接口等)。 4. **集合框架**:深入学习ArrayList、LinkedList、HashSet、HashMap等容器的使用,...
接着,你会接触到多线程,包括线程的创建(Thread类和Runnable接口)、同步机制(synchronized关键字和Lock接口)、线程通信(wait()、notify()和notifyAll()方法)等。这部分内容对于开发高效的并发应用程序至关...
10. **多线程**:解释并发编程的基本概念,如Thread类的使用、同步机制(synchronized关键字和Lock接口)以及线程池。 11. **IO和NIO**:对比传统的I/O模型与非阻塞I/O模型(New Input/Output,NIO)的特点和应用...