java线程锁
出处;https://www.liaoxuefeng.com/wiki/1252599548343744/1306581033549858
java线程锁是重入锁:一个线程可以重复获取锁
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
...
} finally {
lock.unlock();
}
}
使用private final Lock lock = new ReentrantLock();
先去获取锁,然后再去铺获代码块
、最多等待1秒。如果1秒后仍未获取到锁,tryLock()返回false,不会无限等待下去。
所以,使用ReentrantLock比直接使用synchronized更安全,线程在tryLock()失败的时候不会导致死锁
package com.example.demo.config;
public class Thread1 extends Thread{
public void run() {
System.out.println("Thread-1: try get lock A...");
synchronized (Demo.lockA) { //给A加锁
System.out.println("Thread-1: lock A got.");
Demo.sleep1s();
System.out.println("Thread-1: try get lock B...");
synchronized (Demo.lockB) {//获取B锁
System.out.println("Thread-1: lock B got.");
Demo.sleep1s();
}
System.out.println("Thread-1: lock B released.");
}
System.out.println("Thread-1: lock A released.");
}
}
package com.example.demo.config;
public class Thread2 extends Thread{
public void run() {
System.out.println("Thread-2: try get lock B...");
synchronized (Demo.lockB) {//给B加锁
System.out.println("Thread-2: lock B got.");
Demo.sleep1s();
System.out.println("Thread-2: try get lock A...");
synchronized (Demo.lockA) {//获取A锁
System.out.println("Thread-2: lock A got.");
Demo.sleep1s();
}
System.out.println("Thread-2: lock A released.");
}
System.out.println("Thread-2: lock B released.");
}
}
package com.example.demo.config;
public class Demo {
static final Object lockA = new Object();
static final Object lockB = new Object();
private Integer value=0;
private Integer another=0;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public Integer getAnother() {
return another;
}
public void setAnother(Integer another) {
this.another = another;
}
public void add(int m) {
synchronized (lockA) { // 获得lockA的锁
System.out.println("获得lockA的锁0");
this.value += m;
synchronized (lockB) { // 获得lockB的锁
System.out.println("获得lockB的锁0");
this.another += m;
} // 释放lockB的锁
} // 释放lockA的锁
}
public void dec(int m) {
synchronized (lockB) { // 获得lockB的锁
System.out.println("获得lockB的锁1");
this.another -= m;
synchronized (lockA) { // 获得lockA的锁
System.out.println("获得lockA的锁1");
this.value -= m;
} // 释放lockA的锁
} // 释放lockB的锁
}
static void sleep1s() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
Demo demo=new Demo();
try {
demo.add(2);
demo.dec(2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果: Connected to the target VM, address: '127.0.0.1:51315', transport: 'socket' Thread-1: try get lock A... Thread-2: try get lock B... Thread-2: lock B got. Thread-1: lock A got. Thread-1: try get lock B... Thread-2: try get lock A... 造成死锁,
线程1 A加锁后获取B
线程2 B加锁后获取A
造成线程1获取B锁进入等待
线程2获取A锁进入等待,死循环下取
思维没有好的解决办法,只有停止虚拟机进行解决
使用ReentrantLock比直接使用synchronized更安全,可以替代synchronized进行线程同步。 但是,synchronized可以配合wait和notify实现线程在条件不满足时等待,条件满足时唤醒,用ReentrantLock我们怎么编写wait和notify的功能呢? 答案是使用Condition对象来实现wait和notify的功能。 我们仍然以TaskQueue为例,把前面用synchronized实现的功能通过ReentrantLock和Condition来实现
package com.example.demo.config;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private Queue<String> queue = new LinkedList<>();
public void addTask(String s) throws InterruptedException {
lock.lock();
try {
queue.add(s);
sleep();
condition.signalAll();
} finally {
System.out.println("锁取消,全部唤醒");
lock.unlock();
}
}
public String getTask() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
condition.await();
sleep();
System.out.println("等待了");
}
return queue.remove();
} finally {
System.out.println("锁取消,进行等待");
lock.unlock();
}
}
static void sleep() throws InterruptedException {
Thread.sleep(1000);
}
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(30,
100,//当前线程最大并发数
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(30),//候客区
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy() //满了,再进来的线程,不处理,直接抛异常
//new ThreadPoolExecutor.CallerRunsPolicy()//满了,返回回去,这里会让main线程处理
//new ThreadPoolExecutor.DiscardPolicy()//满了,再进来的线程,不处理,不会抛异常
new ThreadPoolExecutor.DiscardOldestPolicy()//满了,再进来的线程,尝试去获取cpu时间片,会与最早的线程竞争,不会抛异常
);
Demo demo = new Demo();
for (int i = 0; i < 9; i++) {
threadPoolExecutor.execute(() -> {
demo.addTask("abcd");
demo.getTask();
});
}
boolean shutdown = threadPoolExecutor.isShutdown();
// threadPoolExecutor.wait(30000);
if (shutdown) {
threadPoolExecutor.shutdown();
}
}
}
可见,使用Condition时,引用的Condition对象必须从Lock实例的newCondition()返回,这样才能获得一个绑定了Lock实例的Condition实例。
Condition提供的await()、signal()、signalAll()原理和synchronized锁对象的wait()、notify()、notifyAll()是一致的,并且其行为也是一样的:
await()会释放当前锁,进入等待状态;
signal()会唤醒某个等待线程;
signalAll()会唤醒所有等待线程;
唤醒线程从await()返回后需要重新获得锁。
此外,和tryLock()类似,await()可以在等待指定时间后,如果还没有被其他线程通过signal()或signalAll()唤醒,可以自己醒来:
if (condition.await(1, TimeUnit.SECOND)) {
// 被其他线程唤醒
} else {
// 指定时间内没有被其他线程唤醒
}
可见,使用Condition配合Lock,我们可以实现更灵活的线程同步。
本文来自博客园,作者:余生请多指教ANT,转载请注明原文链接:https://www.cnblogs.com/wangbiaohistory/p/15206260.html

浙公网安备 33010602011771号