前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java多线程学习(2)-锁的使用

java多线程学习(2)-锁的使用

原创
作者头像
Simon、hao
发布2018-07-14 20:38:07
5421
发布2018-07-14 20:38:07
举报
文章被收录于专栏:java小记java小记

简介

上篇已经对锁的属性做了一个简单的介绍,此篇主要针对于不同锁的使用,分析优缺点,方便以后使用锁的时候能选择合适的锁。引入

准备知识

AQS

在说怎么使用锁之前我想先说下AQS(AbstractQueuedSynchronized),基本上很多同步类都依赖它,AQS维护了一个volatile int state(共享资源的被占用个数)和FIFO一个拥挤堵塞队列

AQS
AQS
CAS

CAS(compare and swap 比较并交换)是乐观锁技术,当多个线程尝试操作同一个共享资源的同时,只有一个线程能够成功,其他线程不会堵塞,而是直接失败,可重复尝试。

工作原理,CAS操作包括三个值,内存位置V,预期原值A,更新值B,线程并发更新共享资源时,会先比较V位置和A的值,如果一样,处理器则将V的值更新成B,不一样处理器则不做任何操作。

可以看下java.util.concurrent包中的AtomicIntege类,看下在不使用锁的情况下是怎么保证线程安全的,以下非标准源码,按照原理写的简易版本

代码语言:txt
复制
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种锁的使用

synchronized
synchronized代码
代码语言:txt
复制
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();
    }
}
main函数
代码语言:txt
复制
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();
        }

    }
}
控制台输出
代码语言:txt
复制
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
  • 从控制台输出可以看出,methodOne进入了methodTwo,说明synchronized是可重入锁;
  • 然后thread1结束之后是5紧接着进入methodOne,说明synchronized不是公平锁;
  • 当一个线程没有结束时,下一个线程是无法进入方法的,说明synchronized是独占锁。
ReentrantLock
实现代码
代码语言:txt
复制
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

代码语言:txt
复制
 ReentrantLock reentrantLock = new ReentrantLock(true);
ReentrantReadWriteLock

此锁能获取两种类型的锁,读锁和写锁,读锁是共享锁,写锁是排他锁,读读共享,读写互斥,此锁也可以构造公平与非公平锁

我们将上面的代码改造使用ReentrantReadWriteLock

代码语言:txt
复制
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函数

代码语言:txt
复制
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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
    • 准备知识
      • AQS
      • CAS
    • 实战
      • synchronized
      • ReentrantLock
      • ReentrantReadWriteLock
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com