标签:状态 去掉 直接 活跃 时代 时间段 util 线程创建 err
大数据时代随之而来的就是并发问题。Java开发本身提供了关于锁的操作。我们知道的有Synchronized。 这个是JVM层面的锁。操作简单
- | await/wait | sleep | yield |
---|---|---|---|
释放锁 | 释放 | 不释放 | 不释放 |
就绪节点 | notify/notifyall方法后 | 休眠时间后 | 立刻就绪 |
提供者 | Object/Condition | Thread | Thread |
代码位置 | 代码块 | 任意 | 任意 |
Object.notify | Object.notifyall | Condition.signal |
---|---|---|
随机唤醒挂载线程之一 | 随机唤醒挂载线程之一 | 按顺序唤醒当前condition上的挂载线程 |
package com.github.zxhtom.lock;
import lombok.Data;
import java.util.concurrent.locks.Lock;
/**
* @author 张新华
* @version V1.0
* @Package com.github.zxhtom.lock
* @date 2020年07月09日, 0009 14:30
* @Copyright ? 2020 安元科技有限公司
*/
public class Counter {
private static Counter util = new Counter();
public static Counter getInstance(){
return util;
}
private int index;
public static Counter getUtil() {
return util;
}
public static void setUtil(Counter util) {
Counter.util = util;
}
public int getIndex() {
return index;
}
public void setIndex(Lock lock , int index) {
/*这里加锁解锁是为了显示可重入性,在外部为加锁解锁*/
lock.lock();
this.index = index;
lock.unlock();
}
}
package com.github.zxhtom.lock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
/**
* @author 张新华
* @version V1.0
* @Package com.github.zxhtom.lock
* @date 2020年07月09日, 0009 14:19
* @Copyright ? 2020 安元科技有限公司
*/
public class LockRunnable implements Runnable {
private Lock lock;
public LockRunnable(Lock lock ) {
this.lock = lock;
}
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
/*lock、unlock之间的业务就能保证同一时刻只有一个线程访问。前提* 是同一个lock对象 , setIndex中也有lock 程序正常运行说明可重* 入
*/
this.lock.lock();
Counter instance = Counter.getInstance();
instance.setIndex(this.lock,instance.getIndex()+1);
this.lock.unlock();
}
}
package com.github.zxhtom.lock;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 张新华
* @version V1.0
* @Package com.github.zxhtom.lock
* @date 2020年07月01日, 0001 14:24
* @Copyright ? 2020 安元科技有限公司
*/
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
int finalI = i;
Thread thread = new Thread(new LockRunnable(lock));
thread.start();
threadList.add(thread);
}
for (Thread thread : threadList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Counter.getInstance().getIndex());
}
}
LockRunnable
中的加锁解锁去掉在运行输出的结果就会少于1000。在Counter中的加锁解锁不去也是会少的。因为那里的加锁解锁只是为了测试可重入性。因为在LockRunnable中的是get、set结合使用的。所以仅仅对set加锁没有用的。场景: 1000个线程按名字的奇偶性分组,奇数一组、偶数一组。奇数执行完之后需要将锁传递给同组的线程 。
package com.github.zxhtom.lock;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @author 张新华
* @version V1.0
* @Package com.github.zxhtom.lock
* @date 2020年07月09日, 0009 14:19
* @Copyright ? 2020 安元科技有限公司
*/
public class LockRunnable implements Runnable {
private Lock lock;
private Condition condition;
private int index;
public LockRunnable(Lock lock , Condition condition,int index) {
this.lock = lock;
this.condition = condition;
this.index = index;
}
@Override
public void run() {
try {
this.lock.lock();
//if (index != 0) {
condition.await();
//}
System.out.println(Thread.currentThread().getName());
Counter instance = Counter.getInstance();
instance.setIndex(this.lock,instance.getIndex()+1);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
this.lock.unlock();
}
}
}
在构建线程的时候传入的Condition是按照序号进行传递的。我们提前准备了两个Condition.一个用来存放奇数好线程(oddCondition)。一个是存储偶数号线程(evenCondition)。
线程创建好之后,这个时候由于LockRunnable中condition.await方法早成线程阻塞了。后面我们通过不同的Condition进行同组线程唤醒。在所有线程结束后我们打印执行数也是1000.我在LockRunnable代码中输出了当前线程名字。我们通过日志发现是oddConditon(奇数条件)线程先输出的。50个奇数执行完了才开始evenCondition(偶数条件)。这是因为我们先oddCondition.signal的。这里读者可以自行执行代码看效果。小编试了试日志输出是分组输出的。
在奇偶添加signal的时候间隔时间一定要足够长。因为在释放锁的时候如果这个时候condition前面的lock会抢锁这样的话就不会是分组了。因为我们为了测试所以这里要足够长
package com.github.zxhtom.lock;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 张新华
* @version V1.0
* @Package com.github.zxhtom.lock
* @date 2020年07月01日, 0001 14:24
* @Copyright ? 2020 安元科技有限公司
*/
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
/*奇数*/
Condition oddCondition = lock.newCondition();
/*偶数*/
Condition evenCondition = lock.newCondition();
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
int finalI = i;
Condition condition = null;
if (i % 2 == 0) {
condition = evenCondition;
} else {
condition = oddCondition;
}
Thread thread = new Thread(new LockRunnable(lock,condition,i));
thread.start();
threadList.add(thread);
}
try {
lock.lock();
oddCondition.signal();
}finally {
lock.unlock();
}
try {
/*休眠足够长,目的是不与前面队列抢锁.可以调更长时间。
* 这样测试准确*/
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
lock.lock();
evenCondition.signal();
}finally {
lock.unlock();
}
for (Thread thread : threadList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Counter.getInstance().getIndex());
}
}
我们通过Lock的lock、unlock就可以灵活的控制并发执行顺序。上面第二个列子如果我们不在finally中执行unlock就会带了很多意想不到的效果,读者可以自己放在一起执行看看效果(在第二个列子中试试).第一个放在一起没问题是因为业务简单没有造成问题的。
Condition条件队列,不同的Condition调用await相当于将当前线程绑定到该Condition上。当Condition唤醒线程内部会将Condition队列等待的节点转移到同步队列上,这里也是为什么上面提到两个Condition间隔时间需要足够长。因为Condition唤醒队列上等待的线程实际上不是真正的唤醒而是件线程添加到通过队列上,借由同步队列的活跃机制唤醒线程的,如果间隔时间不长这个时候回去和刚刚Condition添加过来的线程进行抢锁的。Condition唤醒实际上就是重新竞争一把锁。
标签:状态 去掉 直接 活跃 时代 时间段 util 线程创建 err
原文地址:https://www.cnblogs.com/zhangxinhua/p/13320781.html