一、常用的并发工具类
1.1 CountDownLatch
作用:是一组线程等待其他的线程完成工作以后在执行,加强版join
await用来等待,countDown负责计数器的减一
CountDownLatch代码演示:
package com.thread;
import java.util.concurrent.CountDownLatch;
/**
* 类说明:演示CountDownLatch,有5个初始化的线程,6个扣除点,
* 扣除完毕以后,主线程和业务线程才能继续自己的工作
*/
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化线程(只有一步,有4个)
private static class InitThread implements Runnable {
@Override
public void run() {
System.out.println("InitThread_" + Thread.currentThread().getId());
latch.countDown(); //初始化线程完成工作了,countDown方法只扣减一次;
System.out.println("InitThread_" + Thread.currentThread().getId() + " countDown");
}
}
//业务线程
private static class BusiThread implements Runnable {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("BusiThread_" + Thread.currentThread().getId()
+ " do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
//单独的初始化线程,初始化分为2步,需要扣减两次
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("begin step 1nd.......");
System.out.println("Thread_" + Thread.currentThread().getId());
latch.countDown();//每完成一步初始化工作,扣减一次
System.out.println("Thread_" + Thread.currentThread().getId()
+ " countDown...1");
System.out.println("begin step 2nd.......");
Thread.sleep(1000);
latch.countDown();//每完成一步初始化工作,扣减一次
System.out.println("Thread_" + Thread.currentThread().getId()
+ " countDown...2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new BusiThread()).start();
for (int i = 0; i <= 3; i++) {
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("Main do ites work........");
}
}
输出结果如下:
"C:\Program Files\Java\jdk1.8.0_131\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.2.6\lib\idea_rt.jar=2491:D:\IntelliJ IDEA 2017.2.6\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\projectCode\springBoot\target\classes" com.thread.UseCountDownLatch
InitThread_14
InitThread_16
InitThread_15
InitThread_16 countDown
InitThread_14 countDown
InitThread_17
InitThread_15 countDown
InitThread_17 countDown
begin step 1nd.......
Thread_12
Thread_12 countDown...1
begin step 2nd.......
Thread_12 countDown...2
Main do ites work........
BusiThread_13 do business-----
BusiThread_13 do business-----
BusiThread_13 do business-----
Process finished with exit code 0
latch.await();类似于是一把自动锁,只有CountDownLatch的扣除数被扣除完毕后才能继续执行await()方法后面的代码。
1.2 CyclicBarrier
让一组线程达到某个屏障,被阻塞,一直到组内最后一个线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行CyclicBarrier(int parties)
CyclicBarrier(int parties, Runnable barrierAction),屏障开放,barrierAction定义的任务会执行
CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制,CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件》=线程数,CyclicBarrier放行条件=线程数
package com.thread;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
public class UseCyclicBarrier {
private static CyclicBarrier barrier
= new CyclicBarrier(5, new CollectThread());
private static ConcurrentHashMap<String, Long> resultMap
= new ConcurrentHashMap<>();//存放子线程工作结果的容器
public static void main(String[] args) {
for (int i = 0; i <= 4; i++) {
Thread thread = new Thread(new SubThread());
thread.start();
}
}
//负责屏障开放以后的工作
private static class CollectThread implements Runnable {
@Override
public void run() {
StringBuilder result = new StringBuilder();
for (Map.Entry<String, Long> workResult : resultMap.entrySet()) {
result.append("[" + workResult.getValue() + "]");
}
System.out.println(" the result = " + result);
System.out.println("do other business........");
}
}
//工作线程
private static class SubThread implements Runnable {
@Override
public void run() {
long id = Thread.currentThread().getId();//线程本身的处理结果
resultMap.put(Thread.currentThread().getId() + "", id);
Random r = new Random();//随机决定工作线程的是否睡眠
try {
if (r.nextBoolean()) {
Thread.sleep(2000 + id);
System.out.println("Thread_" + id + " ....do something ");
}
System.out.println(id + "....is await");
barrier.await();
Thread.sleep(2000 + id);
System.out.println("Thread_" + id + " ....do its business ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
"C:\Program Files\Java\jdk1.8.0_131\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.2.6\lib\idea_rt.jar=3110:D:\IntelliJ IDEA 2017.2.6\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\projectCode\springBoot\target\classes" com.thread.UseCyclicBarrier
16....is await
Thread_12 ....do something
12....is await
Thread_13 ....do something
13....is await
Thread_14 ....do something
14....is await
Thread_15 ....do something
15....is await
the result = [12][13][14][15][16]
do other business........
Thread_12 ....do its business
Thread_13 ....do its business
Thread_14 ....do its business
Thread_15 ....do its business
Thread_16 ....do its business
Process finished with exit code 0
结果:
必须等所有线程到达barrier.await()方法时,才能执行new CollectThread()的任务
CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制(只要线程中调用相同对象的countDown方法,就会扣减一次),CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件》=线程数,CyclicBarrier放行条件=线程数
1.3 Semaphore
控制同时访问某个特定资源的线程数量,用在流量控制
Semaphore代码如下:
package com.thread;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
/**
* 类说明:演示Semaphore用法,一个数据库连接池的实现
*/
public class DBPoolSemaphore {
private final static int POOL_SIZE = 10;
private final Semaphore useful,useless;//useful表示可用的数据库连接,useless表示已用的数据库连接
public DBPoolSemaphore() {
this. useful = new Semaphore(POOL_SIZE);
this.useless = new Semaphore(0);
}
//存放数据库连接的容器
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//初始化池
static {
for (int i = 0; i < POOL_SIZE; i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
/*归还连接*/
public void returnConnect(Connection connection) throws InterruptedException {
if(connection!=null) {
System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!"
+"可用连接数:"+useful.availablePermits());
useless.acquire();// 阻塞式方法
synchronized (pool) {
pool.addLast(connection);
}
useful.release();
}
}
/*从池子拿连接*/
public Connection takeConnect() throws InterruptedException {
useful.acquire();
Connection conn;
synchronized (pool) {
conn = pool.removeFirst();
}
useless.release();
return conn;
}
}
package com.xiangxue.ch2.tools.semaphore;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
*
*类说明:数据库连接的平庸实现
*/
public class SqlConnectImpl implements Connection{
// 复写方法太多
}
package com.xiangxue.ch2.tools.semaphore;
import java.sql.Connection;
import java.util.Random;
import com.xiangxue.tools.SleepTools;
/**
*
*类说明:测试数据库连接池
*/
public class AppTest {
private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
//业务线程
private static class BusiThread extends Thread{
@Override
public void run() {
Random r = new Random();//让每个线程持有连接的时间不一样
long start = System.currentTimeMillis();
try {
Connection connect = dbPool.takeConnect();
System.out.println("Thread_"+Thread.currentThread().getId()
+"_获取数据库连接共耗时【"+(System.currentTimeMillis()-start)+"】ms.");
SleepTools.ms(100+r.nextInt(100));//模拟业务操作,线程持有连接查询数据
System.out.println("查询数据完成,归还连接!");
dbPool.returnConnect(connect);
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
Thread thread = new BusiThread();
thread.start();
}
}
}
acquire()需要消费一个许可数(阻塞式方法)
release()添加一个许可数
1.4 Exchange
两个线程间的数据交换,
Exchange代码如下:
使用于两个线程间的数据交换
package com.xiangxue.ch2.tools;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Exchanger;
/**
*
*类说明:Exchange的使用
*/
public class UseExchange {
private static final Exchanger<Set<String>> exchange
= new Exchanger<Set<String>>();
public static void main(String[] args) {
//第一个线程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setA = new HashSet<String>();//存放数据的容器
try {
/*添加数据
* set.add(.....)
* */
setA = exchange.exchange(setA);//交换set
/*处理交换后的数据*/
} catch (InterruptedException e) {
}
}
}).start();
//第二个线程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setB = new HashSet<String>();//存放数据的容器
try {
/*添加数据
* set.add(.....)
* set.add(.....)
* */
setB = exchange.exchange(setB);//交换set
/*处理交换后的数据*/
} catch (InterruptedException e) {
}
}
}).start();
}
}
1.5 Callable、Future和FutureTask*
isDone,结束,正常还是异常结束,或者自己取消,返回true;
isCancelled 任务完成前被取消,返回true;
cancel(boolean):
1、任务还没开始,返回false
2、任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
3、任务已经结束,返回false
包含图片和文字的文档的处理:图片(云上),可以用future去取图片,主线程继续解析文字。
Callable、Future和FutureTask 代码如下:
package com.xiangxue.ch2.future;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import com.xiangxue.tools.SleepTools;
/**
*
*类说明:演示Future等的使用
*/
public class UseFuture {
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算");
Thread.sleep(2000);
for(int i=0;i<5000;i++) {
sum = sum+i;
}
System.out.println("Callable子线程计算完成,结果="+sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();
FutureTask<Integer> futureTask = new FutureTask<Integer>(useCallable);
new Thread(futureTask).start();
Random r = new Random();
SleepTools.second(1);
if(r.nextBoolean()) {//随机决定是获得结果还是终止任务
System.out.println("Get UseCallable result = "+futureTask.get());
}else {
System.out.println("中断计算");
futureTask.cancel(true);
}
}
}
Atom(不可分割)
什么是原子操作?如何实现原子操作?
syn基于阻塞的锁的机制,1、被阻塞的线程优先级很高,2、拿到锁的线程一直不释放锁怎么办?3、大量的竞争,消耗cpu,同时带来死锁或者其他安全。
CAS的原理
CAS(Compare And Swap),指令级别保证这是一个原子操作
三个运算符: 一个内存地址V,一个期望的值A,一个新值B
基本思路:如果地址V上的值和期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作
CAS的问题
A—》B----》A,版本号: A1B2-A3
CAS操作长期不成功,cpu不断的循环,
Jdk中相关原子操作类的使用
AtomicMarkableReference,boolean 有没有动过
AtomicStampedReference 动过几次
代码如下:
package com;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
*
* 当使用锁开销比较大,可以使用原子操作类
*/
public class AtomicArray {
static int[] value = new int[] { 1, 2 };
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
public static void main(String[] args) {
ai.getAndSet(0, 3);
System.out.println(ai.get(0));
System.out.println(value[0]);
}
}
package com;
import java.util.concurrent.atomic.AtomicInteger;
public class UseAtomicInt {
static AtomicInteger ai = new AtomicInteger(10);
public static void main(String[] args) {
System.out.println(ai.getAndIncrement());//10--->11
System.out.println(ai.incrementAndGet());//11--->12--->out
System.out.println(ai.get());
}
}
package com;
import java.util.concurrent.atomic.AtomicReference;
/*
*
*类说明:演示引用类型的原子操作类
*/
public class UseAtomicReference {
static AtomicReference<UserInfo> userRef = new AtomicReference<UserInfo>();
public static void main(String[] args) {
UserInfo user = new UserInfo("Mark", 15);//要修改的实体的实例
userRef.set(user);
UserInfo updateUser = new UserInfo("Bill", 17);//要变化的新实例
userRef.compareAndSet(user, updateUser);
System.out.println(userRef.get().getName());
System.out.println(userRef.get().getAge());
System.out.println(user.getName());
System.out.println(user.getAge());
}
//定义一个实体类
static class UserInfo {
private String name;
private int age;
public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
package com;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
*
*类说明:演示带版本戳的原子操作类
*/
public class UseAtomicStampedReference {
static AtomicStampedReference<String> asr =
new AtomicStampedReference<>("Mark",0);
public static void main(String[] args) throws InterruptedException {
final int oldStamp = asr.getStamp();//那初始的版本号
final String oldReferenc = asr.getReference();
System.out.println(oldReferenc+"==========="+oldStamp);
Thread rightStampThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+"当前变量值:"+oldReferenc+"当前版本戳:"+oldStamp+"-"
+asr.compareAndSet(oldReferenc, oldReferenc+"Java",
oldStamp, oldStamp+1));
}
});
Thread errorStampThread = new Thread(new Runnable() {
@Override
public void run() {
String reference = asr.getReference();
System.out.println(Thread.currentThread().getName()
+"当前变量值:"+reference+"当前版本戳:"+asr.getStamp()+"-"
+asr.compareAndSet(reference, reference+"C",
oldStamp, oldStamp+1));
}
});
rightStampThread.start();
rightStampThread.join();
errorStampThread.start();
errorStampThread.join();
System.out.println(asr.getReference()+"==========="+asr.getStamp());
}
}
一、显式锁
Lock接口和核心方法
package com.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 显示锁的用法
*/
public class LockDemo {
private Lock lock = new ReentrantLock();
private int count;
/**
* 显式锁
*/
private void increatNum() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
/**
* synchronized属于内置锁
*/
private synchronized void increatNum1() {
count++;
}
}
二、lock和synchronized比较
synchronized 代码简洁;
Lock:获取锁可以被中断,超时获取锁,尝试获取锁,读多写少用读写锁;
可重入锁ReentrantLock;
锁的公平和非公平
如果在时间上,先对锁进行获取的请求,一定先被满足,这个锁就是公平的,不满足,就是非公平的。非公平的效率一般来讲更高(假如线程A,B,C分别请求一个锁,如果遇到公平锁的话,A先进入,B被操作系统挂起,C过来时也别挂起,当A执行完毕后,B开始执行,B执行完毕后,C才开始执行;当遇到是非公平锁时,线程A执行完,B这时正在挂起状态,C刚好请求锁,那么C就可以直接拿到锁,B还在挂起)
ReadWriteLock接口和读写锁ReentrantReadWriteLock
ReentrantLock和Syn关键字,都是排他锁,
读写锁:同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞,最适宜读多写少的情况
package com.lock;
public class GoodsInfo {
private final String name;
private double totalMoney;//总销售额
private int storeNumber;//库存数
public GoodsInfo(String name, int totalMoney, int storeNumber) {
this.name = name;
this.totalMoney = totalMoney;
this.storeNumber = storeNumber;
}
public double getTotalMoney() {
return totalMoney;
}
public int getStoreNumber() {
return storeNumber;
}
public void changeNumber(int sellNumber){
this.totalMoney += sellNumber*25;
this.storeNumber -= sellNumber;
}
}
package com.lock;
public interface GoodsService {
public GoodsInfo getNum() throws InterruptedException;//获得商品的信息
public void setNum(int number) throws InterruptedException;//设置商品的数量
}
package com.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class UseRwLock implements GoodsService {
private GoodsInfo goodsInfo;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock();//读操作的锁
private final Lock setLock = lock.writeLock();//写操作的锁
public UseRwLock(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public GoodsInfo getNum() throws InterruptedException {
getLock.lock();
try {
Thread.sleep(1000);
return this.goodsInfo;
}finally {
getLock.unlock();
}
}
@Override
public void setNum(int number) throws InterruptedException {
setLock.lock();
try {
Thread.sleep(1000);
goodsInfo.changeNumber(number);
}finally {
setLock.unlock();
}
}
}
package com.lock;
/**
*
*类说明:用内置锁来实现商品服务接口
*/
public class UseSyn implements GoodsService {
private GoodsInfo goodsInfo;
public UseSyn(GoodsInfo goodsInfo) {
this.goodsInfo = goodsInfo;
}
@Override
public synchronized GoodsInfo getNum() throws InterruptedException {
Thread.sleep(1000);
return this.goodsInfo;
}
@Override
public synchronized void setNum(int number) throws InterruptedException {
Thread.sleep(1000);
goodsInfo.changeNumber(number);
}
}
三、了解LockSupport工具
park开头的方法
负责阻塞线程
unpark(Thread thread)方法
负责唤醒线程
Condition接口
用Lock和Condition实现等待通知(墙裂推荐:https://blog.csdn.net/fuyuwei2015/article/details/72602182)
五、各种锁的区别
ReentrantLock 锁的可重入实现(可重入锁:一个线程可以多次获取锁)
当每获取一次锁,都会给state加acquires(代码请求锁的次数)
java.util.concurrent.locks.AbstractQueuedSynchronizer#state
java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
当每释放一次锁,都要给state减去释放的次数
公平锁和非公平锁的区别
如果是公平锁获取锁时先要调用此方法hasQueuedThreads(),查看是否有前驱节点,如果有先让前驱节点获取,非公平不会调用此方法、
读写锁(ReentrantReadWriteLock.class)
一个同步器如何表示锁的两个状态??
java.util.concurrent.locks.AbstractQueuedSynchronizer#state
private volatile int state;
该变量是int类型,32位,高16表示读线程,有个本地变量保存读线程访问锁的次数,低16位表示写线程。
写锁可以降低为读锁,但是读锁不能升为写锁。
独占锁和共享锁:
一个锁只能被一个线程占用
一个锁可以被多个线程占用(三元共享同步工具类)