本文目录一览:
1、如何使用java的锁机制
2、java Lock锁为什么不直接使用
3、Java中lock类该怎么用啊?可以完全代替synchronize吗?
4、关于java的lock和condition
5、Java中Lock,tryLock,lockInterruptibly有什么区别
6、深入研究 Java Synchronize 和 Lock 的区别与用法
如何使用java的锁机制
可以在临界区代码开始的位置执行Lock类的lock
方法,为代码块加锁,而在临界区的出口使用相同Lock实例的unlock
方法,释放临界区资源。
Demo2-12中,主线程先创建了一个lockTest
对象test
,然后将相同的test
对象交给两个不同的线程执行。子线程1获取到了锁后,开始执行before sleep
输出语句,遇到sleep
后,线程1阻塞将会放弃执行权,这时线程2可以获取执行权,当线程2执行lock
方法时,发现锁已经被别的线程获取,所以线程2阻塞等待锁的释放。线程1从sleep
中被唤醒后,将继续执行after sleep
语句,之后释放了锁,此时线程2从锁等待中被唤醒,执行临界区的内容,因此Demo2-12的输出是先线程1的两条语句,之后才输出线程2的两条语句。而Demo2-13在没有锁的保护下,程序无法保证先将线程1的两条语句输出后再执行线程2的输出,因此,Demo2-13的输出结果是交叉的。
java Lock锁为什么不直接使用
主要为了多线程访问共享资源时,保证只能有一个线程操作资源,比如说一个servlet中根据参数对一个公共变量设置值,如果不采用Lock那么在并发访问时就无法保证每个线程中公共变量设置的值都是各自线程的,在后续的应用中变量的值可能会错乱,加了Lock之后就保证了在一个线程中从头到尾都是一致的。
Java中lock类该怎么用啊?可以完全代替synchronize吗?
- 上锁(漂亮的写法是先判断是否锁被占用)
- 你要执行的命令(比如读取IO和数据库,释放相关资源)
- 释放锁
然后别的地方就是重复以上三步的过程。synchronize
就是把1和3替换成了{}
。 完全替代不一定,毕竟synchronize
使用起来简单明了。
关于java的lock和condition
- 在某些情况下,当内部锁非常不灵活时,显式锁就可以派上用场。内部条件队列有一些缺陷,每个内部锁只能有一个与之相关联的条件队列。
- 使用显式的
Lock
和Condition
的实现类提供了一个比内部锁和条件队列更加灵活的选择。一个Condition
和一个单独的Lock
相关联,就像条件队列和单独的内部锁相关联一样。每个锁可以有多个等待集、中断性选择、基于时限、公平性选择等。
public interface Condition {
void await() throws InterruptedException; // 相当于wait
boolean await(long time, TimeUnit unit) throws InterruptedException;
long awaitNanos(long nanosTimeout) throws InterruptedException;
void awaitUninterruptibly();
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal(); // 相当于notify
void signalAll(); // 相当于notifyAll
}
调用与Condition
相关联的Lock
的Lock.newCondition()
方法,可创建一个Condition
。
3. 有限缓存操作
@ThreadSafe
public class ConditionBoundedBuffer<T> {
protected final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
@GuardedBy("lock")
private final T[] items = (T[]) new Object[BUFFER_SIZE];
@GuardedBy("lock")
private int tail, head, count;
public void put(T x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[tail] = x;
if (++tail == items.length)
tail = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
T x = items[head];
items[head] = null;
if (++head == items.length)
head = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
Java中Lock,tryLock,lockInterruptibly有什么区别
Java中Lock
、tryLock
、lockInterruptibly
的区别如下:
一、lock()
方法
使用lock()
获取锁,若获取成功,标记下是该线程获取到了锁(用于锁重入),然后返回。若获取失败,这时跑一个for循环,循环中先将线程阻塞放入等待队列,当被调用signal()
时线程被唤醒,这时进行锁竞争(因为默认使用的是非公平锁),如果此时用CAS获取到了锁那么就返回,如果没获取到那么再次放入等待队列,等待唤醒,如此循环。其间就算外部调用了interrupt()
,循环也会继续走下去。一直到当前线程获取到了这个锁,此时才处理interrupt
标志,若有,则执行 Thread.currentThread().interrupt()
,结果如何取决于外层的处理。
最终执行的方法如下:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 如果竞争得到了锁
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted; // 获取成功返回interrupted标志
}
// 只修改标志位,不做其他处理
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
其中parkAndCheckInterrupt()
调用了LockSupport.park()
,该方法使用Unsafe
类将进程阻塞并放入等待队列,等待唤醒,和await()
有点类似。
可以看到循环中检测到了interrupt
标记,但是仅做 interrupted = true
操作,直到获取到了锁,才return interrupted
,然后处理如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); // 执行Thread.currentThread().interrupt()
}
二、lockInterruptibly()
方法
和lock()
相比,lockInterruptibly()
只有略微的修改,for循环过程中,如果检测到interrupt
标志为true
,则立刻抛出InterruptedException
异常,这时程序通过异常直接返回到最外层了,由外层继续处理,因此使用lockInterruptibly()
时必须捕捉异常。
最终执行的方法如下:
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return; // 获取成功返回
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
throw new InterruptedException(); // 直接抛出异常
}
} finally {
if (failed)
cancelAcquire(node);
}
}
三、tryLock()
方法
使用tryLock()
尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true
;若获取失败,此时直接返回false
,告诉外层没有获取到锁,之后的操作取决于外层。
代码如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
深入研究 Java Synchronize 和 Lock 的区别与用法
一、synchronized和lock的用法区别
synchronized
:在需要同步的对象中加入此控制,synchronized
可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。lock
:需要显示指定起始位置和终止位置。一般使用ReentrantLock
类作为锁,多个线程中必须要使用一个ReentrantLock
类作为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()
和unlock()
显示指出。所以一般会在finally
块中写unlock()
以防死锁。
二、synchronized和lock用途区别
synchronized
原语和ReentrantLock
在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock
,特别是遇到下面2种需求的时候:
- 某个线程在等待一个锁的控制权的这段时间需要中断。
- 需要分开处理一些
wait-notify
,ReentrantLock
里面的Condition
应用,能够控制notify
哪个线程。 - 具有公平锁功能,每个到来的线程都将排队等候。