上篇已经对锁的属性做了一个简单的介绍,此篇主要针对于不同锁的使用,分析优缺点,方便以后使用锁的时候能选择合适的锁。引入
在说怎么使用锁之前我想先说下AQS(AbstractQueuedSynchronized),基本上很多同步类都依赖它,AQS维护了一个volatile int state(共享资源的被占用个数)和FIFO一个拥挤堵塞队列
CAS(compare and swap 比较并交换)是乐观锁技术,当多个线程尝试操作同一个共享资源的同时,只有一个线程能够成功,其他线程不会堵塞,而是直接失败,可重复尝试。
工作原理,CAS操作包括三个值,内存位置V,预期原值A,更新值B,线程并发更新共享资源时,会先比较V位置和A的值,如果一样,处理器则将V的值更新成B,不一样处理器则不做任何操作。
可以看下java.util.concurrent包中的AtomicIntege类,看下在不使用锁的情况下是怎么保证线程安全的,以下非标准源码,按照原理写的简易版本
public class AtomicInteger extends Number implements java.io.Serializable {
//需要修改的值v
public volatile int v;
//获取v的值
public final int get(){
return v;
}
//i为新值,var2是实例域的偏移量
public final int getAndIncrement(int i,long var2){
int resultV;
do{
resultV = get();
}while(!unsafe.compareAndSwapInt(this, var2, resultV, i))
return resultV;
}
}
下面将分别演示synchronized、ReentrantLock3种锁的使用
public class SimpleSynchronized implements Runnable {
public synchronized void methodOne(){
System.out.println("methodOne:"+Thread.currentThread().getName());
methodTwo();
System.out.println("will leave:"+Thread.currentThread().getName());
}
public synchronized void methodTwo(){
System.out.println("methodTwo:"+Thread.currentThread().getName());
}
@Override
public void run() {
methodOne();
}
}
public class Main {
public static void main(String[] args) {
SimpleSynchronized simpleSynchronized = new SimpleSynchronized();
for (int i= 0 ;i < 5;i++){
new Thread(simpleSynchronized,"thread-"+(i+1)).start();
}
}
}
methodOne:thread-1
methodTwo:thread-1
will leave:thread-1
methodOne:thread-5
methodTwo:thread-5
will leave:thread-5
methodOne:thread-4
methodTwo:thread-4
will leave:thread-4
methodOne:thread-3
methodTwo:thread-3
will leave:thread-3
methodOne:thread-2
methodTwo:thread-2
will leave:thread-2
public class SimpleReentrantLock implements Runnable{
private ReentrantLock reentrantLock = new ReentrantLock();
public void methodOne(){
System.out.println("enter methodOne"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
System.out.println("methodOne:"+Thread.currentThread().getName());
methodTwo();
System.out.println("will unlock:"+Thread.currentThread().getName());
reentrantLock.unlock();
System.out.println("after unlock:"+Thread.currentThread().getName());
}
public void methodTwo(){
System.out.println("enter methodTwo"+Thread.currentThread().getName());
reentrantLock.lock();
System.out.println("methodTwo:"+Thread.currentThread().getName());
reentrantLock.unlock();
System.out.println("after unlock:"+Thread.currentThread().getName());
}
@Override
public void run() {
methodOne();
}
}
执行结果较长,不作粘贴了,从结果能看出,和synchronized差不多,是重入锁;
不过reentrantLock是即可构造公平锁,也可构造非公平锁的,默认为非公平锁,构造公平锁只需要在构造方法中传入true
ReentrantLock reentrantLock = new ReentrantLock(true);
此锁能获取两种类型的锁,读锁和写锁,读锁是共享锁,写锁是排他锁,读读共享,读写互斥,此锁也可以构造公平与非公平锁
我们将上面的代码改造使用ReentrantReadWriteLock
public class SimpleReentrantReadWriteLock implements Runnable {
static List<String> list = new ArrayList<>();
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock r = rwl.readLock();
static Lock w = rwl.writeLock();
private int var1;
private String var2;
public SimpleReentrantReadWriteLock(int var1,String var2) {
this.var1 = var1;
this.var2 = var2;
}
@Override
public void run() {
switch (var1){
case 1:
read();
break;
case 2:
write(var2);
break;
case 3:
clearArg();
break;
default:
read();
break;
}
}
public void read(){
try {
r.lock();
System.out.println("进入读函数了:"+Thread.currentThread().getName());
if (list.size()>0)
System.out.println("list有值了:"+list);
}catch (Exception e){
}finally {
r.unlock();
}
}
public void write(String value){
w.lock();
try {
System.out.println("进入写函数了:" + Thread.currentThread().getName());
list.add(value);
r.lock();
w.unlock();
//释放写锁,获取读锁
Thread.sleep(2000);
System.out.println("释放写锁");
}catch (Exception e){
System.out.println(e);
}finally {
r.unlock();
}
}
public void clearArg(){
w.lock();
System.out.println("清空队列:"+Thread.currentThread().getName());
list.clear();
w.unlock();
}
}
main函数
public class Main {
public static void main(String[] args) {
SimpleReentrantReadWriteLock slock1 = new SimpleReentrantReadWriteLock(1,null);
SimpleReentrantReadWriteLock slock2 = new SimpleReentrantReadWriteLock(2,"a");
SimpleReentrantReadWriteLock slock3 = new SimpleReentrantReadWriteLock(3,null);
for (int i= 0 ;i < 10;i++){
new Thread(slock1,"slock1-thread-"+(i+1)).start();
new Thread(slock2,"slock2-thread-"+(i+1)).start();
new Thread(slock3,"slock3-thread-"+(i+1)).start();
}
}
}
从此代码的运行结果我们可以发现:
下面将准备线程池方面的知识,yeah!!!!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。