精华内容
下载资源
问答
  • ET199写锁教程广联达写锁教程376驱动安装教程 空没法,升级前驱动低于296不进。总结就是没底层和底层太旧的别下!! 下载地址在压缩包里面,文件太大传不
  • Java读写源码解析

    千次阅读 2019-04-27 17:49:12
    在AQS的实现类ReentrantReadWriteLock$Sync中,将32位int类型的state分成2部分,高位的16个2进制位表示读,低位的16个2进制位表示写锁。即读写锁的并发最多只能达到2的16次方减去1。 0000000000000000 ...

    ReentrantReadWriteLock读写锁类结构图:

    AQS如何用一个int值表示读写锁的2种状态?

        在AQS的实现类ReentrantReadWriteLock$Sync中,将32位int类型的state分成2部分,高位的16个2进制位表示读锁,低位的16个2进制位表示写锁。即读锁和写锁的并发最多只能达到2的16次方减去1。

        0000000000000000   0000000000000000

        ---------------------------------------------------------------------------------

        如果是读锁进入,则变成:

        0000000000000001    0000000000000000

        即将原有的state值加上65535(即2的16次方-1)

        如何获取读锁数量:

        将state的高位右移16位,则变成:

        0000000000000000   0000000000000001

        -------------------------------------------------------------------------------------

        如果是写锁进入,则变成:

        0000000000000000   0000000000000001

        即将原有的state值加1

        如何获取写锁数量:

        将state高位并上16个0,低位并上16个1(2的16次方减1),0&0或者1始终都为0,即将高位变成0,1&1为1,则低位并上16个1值不变

        0000000000000000    0000000000000001

        0000000000000000    1111 1111 1111 1111

       =0000000000000000    0000000000000001

    对应的源码如下:

    static final int SHARED_SHIFT   = 16;
    static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;//读锁的最大并发量
    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//写锁的最大并发量
    //获取读锁数量  将state的高位右移16位
    static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
    //获取写锁数量  将state的2进制并上16个1(2的16次方减1)
    static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

    读锁重入实现原理:

    ReentrantLock$Sync中的AQS的state表示的是重入次数,因为这个锁是独占锁,只有获取到锁的线程才能执行,才能重入,其他线程只能等待锁释放后去抢占或者排队等候

    而ReentrantReadWriteLock$Sync中,AQS的state不在表示重入次数,它表示的是当前有多少读锁和多少写锁,假设没有写锁,那么所有的读锁都能并发访问数据;如果有写锁在排队,则后来的读锁、写锁也需要排队,因为读写互斥、写写互斥

    在ReentrantReadWriteLock$Sync中,有一个ThreadLocalHoldCounter类型的属性,该属性是ThreadLocal<HoldCounter>的子类,而HoldCounter类型才是记录线程重入次数的类。具体源码如下:

    private transient ThreadLocalHoldCounter readHolds;
    static final class ThreadLocalHoldCounter
        extends ThreadLocal<HoldCounter> {
        public HoldCounter initialValue() {
            return new HoldCounter();
        }
    }
    static final class HoldCounter {
        int count = 0;
        // Use id, not reference, to avoid garbage retention
        final long tid = getThreadId(Thread.currentThread());
    }

    读锁源码解析:

    ReadWriteLock rwlock=new ReentrantReadWriteLock();//不带参数或者参数为false,表示非公平锁
    Lock rlock=rwlock.readLock();//获取读锁
    new Thread(()->{
    	rlock.lock();
    			
    	rlock.unlock();
    }).start();
    
    
    // ReentrantReadWriteLock  ReadLock
    public void lock() {
         sync.acquireShared(1);//调用sync尝试获取读锁
    }
    //AQS
    public final void acquireShared(int arg) {
        //tryAcquireShared是一个抽象方法
        //尝试获取锁,如果返回值为-1表示没有获取到锁,如果返回值为1表示获取到了锁
        if (tryAcquireShared(arg) < 0)
            //将当前线程放到队列中
            doAcquireShared(arg);
    }
    //ReentrantReadWriteLock Sycn
    /**
    * 1.如果写锁被另外一个线程持有,则返回-1
    * 2.如果获取到了锁,则返回1
    * 3.步骤2失败,则循环重试
    **/
    protected final int tryAcquireShared(int unused) {
       
        Thread current = Thread.currentThread();
        int c = getState();//获取状态
        //如果写锁被另外一个线程持有,则返回-1
        if (exclusiveCount(c) != 0 &&
            getExclusiveOwnerThread() != current)
            return -1;
        int r = sharedCount(c);
        //公平锁时:如果队列为空或者队列第一个节点是当前线程,返回false
        //         如果队列队列不为空,则返回true,表示要规规矩矩的排队
        //非公平锁时:如果队列为空或者前面没有写锁在排队时,则返回false
        //           如果头结点不为空,下一个结点也不为空并且是写锁时,返回true,表示要规规矩矩的排队
        if (!readerShouldBlock() &&
            r < MAX_COUNT &&
            compareAndSetState(c, c + SHARED_UNIT)) {
            if (r == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                //记录读锁重入次数相关操作
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    cachedHoldCounter = rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
            }
            return 1;
        }
        return fullTryAcquireShared(current);
    }
    
    
    // ReentrantReadWriteLock  ReadLock
    public void unlock() {
        sync.releaseShared(1);
    }
    //AQS
    public final boolean releaseShared(int arg) {
        //抽象方法
        if (tryReleaseShared(arg)) {//尝试释放读锁,该方法主要是在操纵重入次数,如果次数为0,则返回true
            doReleaseShared();
            return true;
        }
        return false;
    }
    //ReentrantReadWriteLock Sync
    protected final boolean tryReleaseShared(int unused) {
        Thread current = Thread.currentThread();
        //如果当前线程是第一个读锁
        if (firstReader == current) {
            // 如果重入次数为1,表示没有重入过
            if (firstReaderHoldCount == 1)
                firstReader = null;
            else//如果重入过,则重入次数减一
                firstReaderHoldCount--;
        } else {
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                rh = readHolds.get();
            //获取到重入次数
            int count = rh.count;
            if (count <= 1) {
                //如果重入次数为1,表示可以真正的释放锁了
                readHolds.remove();
                if (count <= 0)
                    throw unmatchedUnlockException();
            }
            //如果重入过,则重入次数减一
            --rh.count;
        }
        for (;;) {
            int c = getState();
            int nextc = c - SHARED_UNIT;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

    写锁源码解析:

    ReadWriteLock rwlock=new ReentrantReadWriteLock();
    Lock wlock=rwlock.writeLock();
    new Thread(()->{
    	wlock.lock();
    	
    	wlock.unlock();
    }).start();
    
    //WriteLock
    public void lock() {
        sync.acquire(1);
    }
    //AQS
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&//尝试获取写锁
            //这里调用的是AQS的排队相关方法,因为写锁是互斥的
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    //ReentrantReadWriteLock$Sync
    protected final boolean tryAcquire(int acquires) {
        Thread current = Thread.currentThread();
        int c = getState();
        int w = exclusiveCount(c);//获取写锁数量
        //如果AQS的state状态不为0,表示队列中有锁,可能是读锁,也可能是写锁
        if (c != 0) {
            //如果写锁为0 或者当前线程不是排队中写锁的线程,返回false
            if (w == 0 || current != getExclusiveOwnerThread())
                return false;
            if (w + exclusiveCount(acquires) > MAX_COUNT)
                throw new Error("Maximum lock count exceeded");
            //表示当前的写锁是重入进来的,那么返回true
            setState(c + acquires);
            return true;
        }
        //表示队列中没有锁,这个写锁是第一个锁
        //公平锁时:如果队列为空或者队列第一个节点是当前线程,返回false
        //         如果队列队列不为空,则返回true,表示要规规矩矩的排队
        //非公平锁:直接返回false
        if (writerShouldBlock() ||
            !compareAndSetState(c, c + acquires))//当前锁的CAS释放失败
            return false;
        setExclusiveOwnerThread(current);
        //返回true表示获取到了写锁
        return true;
    }

    总结:

    1、读写锁适合读多写少的场景,读读并行,读写、写写互斥

    2、读写锁使用一个int类型的state变量,实现了2种锁状态,低16位记录写锁数量,高16位记录读锁数量,而不是记录的重入次数。获取读锁数量时,需要将state的2进制位右移16位;获取写锁数量时,需要将state的2进制位与上前16位0后16位1

    3、在读锁或者写锁中,需要记录某个线程的重入次数,使用到了ThreadLocalHoldCounter类,该类是ThreadLocal<HoldCounter>的子类,而HoldCounter类型才是记录线程重入次数的类。

    4、不管是读锁还是写锁,在获取锁的时候,都有公平或非公平2种方式,这取决于创建ReentrantReadWriteLock的创建方式

    5、读锁加锁过程:

        公平锁时:

            如果队列不为空且当前线程不是队列的首结点,则表示要规规矩矩的排队,否则执行后面的CAS操作

        非公平锁时:

            如果头结点不为空,下一个结点是写锁时,表示要规规矩矩的排队;否则执行CAS操作

    6、写锁加锁过程:如果队列中有读锁或者写锁时,需要排队,如果队列为空,则直接获取到写锁

         公平锁时:

                 如果队列不为空且当前线程不是队列的第一个结点,则表示要规规矩矩的排队,否则执行后面的CAS操作

         非公平锁:

                如果队列为空时,直接执行CAS操作

     

    展开全文
  • 重要声明:本人之前对java中的读写也不是非常了解,用的也不是很多,尤其在读写锁的策略原理一块没有深究过,本篇文章是在学习【玩转Java并发工具,精通JUC,成为并发多面手】课程后的,故文章类型选择为"转载",...

    重要声明:本人之前对java中的读写锁也不是非常了解,用的也不是很多,尤其在读写锁的策略原理一块没有深究过,本篇文章是在学习【玩转Java并发工具,精通JUC,成为并发多面手】课程后写的,故文章类型选择为"转载",因为本文的很多结论都是来自于那门课程,请知悉~。希望对各位同仁有帮助~


    books 读写锁的基本使用

    在【ReentrantLock锁详解】一文中讲到了java中锁的划分,本篇主要讲述共享锁和排他锁:ReentrantReadWriteLock

    在ReentrantReadWriteLock中包含读锁和写锁,其中读锁是可以多线程共享的,即共享锁,而写锁是排他锁,在更改时候不允许其他线程操作。读写锁其实是一把锁,所以会有同一时刻不允许读写锁共存的规定。之所以要细分读锁和写锁也是为了提高效率,将读和写分离,对比ReentrantLock就可以发现,无论并发读还是写,它总会先锁住全部再说。

    接着先来个代码演示下,读锁是共享锁,写锁是排他锁:

    /**
     * ReentrantReadWriteLock读写锁示例
     **/
    public class ReentrantReadWriteLockTest {
    
        private static ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
        private static ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();
        private static ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock();
    
        public static void read() {
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取读锁,开始执行");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println(Thread.currentThread().getName() + "释放读锁");
            }
        }
    
        public static void write() {
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取写锁,开始执行");
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
                System.out.println(Thread.currentThread().getName() + "释放写锁");
            }
        }
    
        public static void main(String[] args) {
            new Thread(() -> read(), "Thread1").start();
            new Thread(() -> read(), "Thread2").start();
            new Thread(() -> write(), "Thread3").start();
            new Thread(() -> write(), "Thread4").start();
        }
    }

    输出结果如下,线程1和线程2可以同时获取读锁,而线程3和线程4只能依次获取写锁,因为线程4必须等待线程3释放写锁后才能获取到锁:

    Thread1获取读锁,开始执行
    Thread2获取读锁,开始执行
    Thread1释放读锁
    Thread2释放读锁
    Thread3获取写锁,开始执行
    Thread3释放写锁
    Thread4获取写锁,开始执行
    Thread4释放写锁

    books 读锁的插队策略

    设想如下场景:在非公平的ReentrantReadWriteLock锁中,线程2和线程4正在同时读取,线程3想要写入,拿不到锁(同一时刻是不允许读写锁共存的),于是进入等待队列,线程5不在队列里,现在过来想要读取策略1是如果允许读插队,就是说线程5读先于线程3写操作执行,因为读锁是共享锁,不影响后面的线程3的写操作,这种策略可以提高一定的效率,却可能导致像线程3这样的线程一直在等待中,因为可能线程5读操作之后又来了n个线程也进行读操作,造成线程饥饿策略2是不允许插队,即线程5的读操作必须排在线程3的写操作之后,放入队列中,排在线程3之后,这样能避免线程饥饿

    事实上ReentrantReadWriteLock在非公平情况下,读锁采用的就是策略2:不允许读锁插队,避免线程饥饿。更加确切的说是:在非公平锁情况下,允许写锁插队,也允许读锁插队,但是读锁插队的前提是队列中的头节点不能是想获取写锁的线程。

    以上还在非公平ReentrantReadWriteLock锁中,在公平锁中,读写锁都是是不允许插队的,严格按照线程请求获取锁顺序执行。

    下面用代码演示一下结论:在非公平锁情况下,允许写锁插队,也允许读锁插队,但是读锁插队的前提是队列中的头节点不能是想获取写锁的线程

    /**
     * ReentrantReadWriteLock读写锁插队策略测试
     **/
    public class ReentrantReadWriteLockTest {
    
        private static ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
        private static ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();
        private static ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock();
    
        public static void read() {
            System.out.println(Thread.currentThread().getName() + "开始尝试获取读锁");
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取读锁,开始执行");
                Thread.sleep(20);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println(Thread.currentThread().getName() + "释放读锁");
            }
        }
    
        public static void write() {
            System.out.println(Thread.currentThread().getName() + "开始尝试获取写锁");
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取写锁,开始执行");
                Thread.sleep(40);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + "释放写锁");
                writeLock.unlock();
            }
        }
    
        public static void main(String[] args) {
            new Thread(() -> write(), "Thread1").start();
            new Thread(() -> read(), "Thread2").start();
            new Thread(() -> read(), "Thread3").start();
            new Thread(() -> write(), "Thread4").start();
            new Thread(() -> read(), "Thread5").start();
            new Thread(() -> {
                Thread[] threads = new Thread[1000];
                for (int i = 0; i < 1000; i++) {
                    threads[i] = new Thread(() -> read(), "子线程创建的Thread" + i);
                }
                for (int i = 0; i < 1000; i++) {
                    threads[i].start();
                }
            }).start();
        }
    }

    以上测试代码就演示了,在非公平锁时,其一:同一时刻读写锁不能同时存在, 其二,读锁非常容易插队,但前提是队列中的头结点不能是想获取写锁的线程。

    books 锁的升降级

    升降级是指读锁升级为写锁,写锁降级为度锁。在ReentrantReadWriteLock读写锁中,只支持写锁降级为读锁,而不支持读锁升级为写锁,如下代码测试所示:

    /**
     * ReentrantReadWriteLock锁升降级测试
     **/
    public class ReentrantReadWriteLockTest {
    
        private static ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
        private static ReentrantReadWriteLock.ReadLock readLock = reentrantLock.readLock();
        private static ReentrantReadWriteLock.WriteLock writeLock = reentrantLock.writeLock();
    
        public static void read() {
            System.out.println(Thread.currentThread().getName() + "开始尝试获取读锁");
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取读锁,开始执行");
                Thread.sleep(20);
                System.out.println(Thread.currentThread().getName()+ "尝试升级读锁为写锁");
                //读锁升级为写锁(失败)
                writeLock.lock();
                System.out.println(Thread.currentThread().getName() +"读锁升级为写锁成功");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println(Thread.currentThread().getName() + "释放读锁");
            }
        }
    
        public static void write() {
            System.out.println(Thread.currentThread().getName() + "开始尝试获取写锁");
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取写锁,开始执行");
                Thread.sleep(40);
                System.out.println(Thread.currentThread().getName() +"尝试降级写锁为读锁");
                //写锁降级为读锁(成功)
                readLock.lock();
                System.out.println(Thread.currentThread().getName()+ "写锁降级为读锁成功");
                System.out.println();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + "释放写锁");
                writeLock.unlock();
                readLock.unlock();
            }
        }
    
        public static void main(String[] args) {
            new Thread(() -> write(), "Thread1").start();
            new Thread(() -> read(), "Thread2").start();
        }
    }

    运行控制台输出:

    Thread1开始尝试获取写锁
    Thread1获取写锁,开始执行
    Thread1尝试降级写锁为读锁
    Thread1写锁降级为读锁成功
    Thread1释放写锁
    
    Thread2开始尝试获取读锁
    Thread2获取读锁,开始执行
    Thread2尝试升级读锁为写锁

    之所以ReentrantReadWriteLock不支持锁的升级(其它锁可以支持),主要是避免死锁,例如两个线程A和B都在读, A升级要求B释放读锁,B升级要求A释放读锁,互相等待形成死循环。如果能严格保证每次都只有一个线程升级那也是可以的。

    books 总结

    1. 读写锁特点特点:读锁是共享锁,写锁是排他锁,读锁和写锁不能同时存在
    2. 插队策略:为了防止线程饥饿,读锁不能插队
    3. 升级策略:只能降级,不能升级
    4. ReentrantReadWriteLock适合于读多写少的场合,可以提高并发效率,而ReentrantLock适合普通场合

    books 引申阅读:

    展开全文
  • 读写

    千次阅读 2019-01-23 09:47:23
    读写的特性 读读是共享的。读写,写写是互斥的。 1.读读互斥例子 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ...

    读写锁的特性

    读读是共享的。读写,写写是互斥的。

    1.读读互斥例子

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class ReentrantReadWriteLockDemo {
    
        private int i = 0;
        private int j = 0;
    
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        Lock readLock = lock.readLock();
        Lock writeLock = lock.writeLock();
    
        public void out(){
            readLock.lock();
            System.out.println(Thread.currentThread().getName()+",(i="+i+"j="+j+")");
            readLock.unlock();
        }
    
        public void increase(){
            writeLock.lock();
            try {
                i++;
                Thread.sleep(500l);
                j++;
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                writeLock.unlock();
            }
    
        }
    
        public static void main(String[] args) {
            ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
    
    
            new Thread(()->{
                reentrantReadWriteLockDemo.increase();
            },"写线程1").start();
    
            new Thread(()->{
                reentrantReadWriteLockDemo.increase();
            },"写线程2").start();
        }
    }
    

    在increase()方法打断点,进行多线程debug,当写线程1获取到写锁时,写线程2尝试去获取锁,显示无法获取锁,进入等待状态,由此说明,写写是互斥的。

    2.读写互斥,我们将main方法如下修改一下,再debug。

     public static void main(String[] args) {
            ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
            
            new Thread(()->{
                reentrantReadWriteLockDemo.increase();
            },"写线程1").start();
    
            new Thread(()->{
                reentrantReadWriteLockDemo.out();
            },"读线程2").start();
        }

    在increase()和out()方法上打上断点,进行多线程debug,如下写线程1和读线程2正常启动

    当写线程1获得锁时,读线程2尝试去获得锁,读线程2拿不到锁,进入等待状态(反之先让读线程2先获取锁时,再让写线程1尝试获得锁的也是一样的,写线程会获取失败进入等待状态,这里就不一一展示了),说明读写也是互斥的。 

     

    3.读读不互斥,我们将mian方法再改一下

    public static void main(String[] args) {
            ReentrantReadWriteLockDemo reentrantReadWriteLockDemo = new ReentrantReadWriteLockDemo();
    
            new Thread(()->{
                reentrantReadWriteLockDemo.out();
            },"读线程1").start();
    
            new Thread(()->{
                reentrantReadWriteLockDemo.out();
            },"读线程2").start();
        }

    debug结果如下,在读线程1获得读锁时,读线程2仍然可以获取到读锁,说明读读是不互斥的 

    4.锁降级

        写锁程序获得写入锁后可以获得读取锁,然后释放写入锁,这样就从写入锁变成读写锁,从而实现读写锁的降级。

    注意

        锁降级之后,血锁并不会直接降成读锁,不会随着读锁的释放而释放,因此需要显示地释放写锁

    简单例子

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class LockDegrade {
    
        public static void main(String[] args) {
            ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
            Lock readLock = lock.readLock();
            Lock writeLock = lock.writeLock();
    
            writeLock.lock();
            readLock.lock();
            writeLock.unlock();
            readLock.unlock();
    
            System.out.println("运行结束");
    
        }
    }

       运行结果如下,主线程获得写锁之后可以获得读锁,并且写锁释放后,后面的释放读锁代码也正常运行,说明读锁在写锁释放后仍然能用。

    锁降级的应用

    数据比较敏感,需要在对数据修改后获取到数据 修改后的值,做其他操作。

    展开全文
  • C++读写介绍

    千次阅读 2019-04-13 14:26:08
    但是这时又有一个线程b试图去读共享变量 i,发现被锁定了,那么b不得不等到a释放了后才能获得并读取 i 的值,但是两个读取操作即使是同时发生的,也并不会像操作那样造成竞争,因为它们不修改变量的值。...

    一 点睛

    先看看互斥锁,它只有两个状态,要么是加锁状态,要么是不加锁状态。假如现在一个线程a只是想读一个共享变量 i,因为不确定是否会有线程去写它,所以我们还是要对它进行加锁。但是这时又有一个线程b试图去读共享变量 i,发现被锁定了,那么b不得不等到a释放了锁后才能获得锁并读取 i 的值,但是两个读取操作即使是同时发生的,也并不会像写操作那样造成竞争,因为它们不修改变量的值。所以我们期望在多个线程试图读取共享变量的时候,它们可以立刻获取因为读而加的锁,而不是需要等待前一个线程释放。

    读写锁可以解决上面的问题。它提供了比互斥锁更好的并行性。因为以读模式加锁后,当有多个线程试图再以读模式加锁时,并不会造成这些线程阻塞在等待锁的释放上。

    读写锁是多线程同步的另外一个机制。在一些程序中存在读操作和写操作问题,对某些资源的访问会存在两种可能情况,一种情况是访问必须是排他的,就是独占的意思,这种操作称作写操作,另外一种情况是访问方式是可以共享的,就是可以有多个线程同时去访问某个资源,这种操作称为读操作。这个问题模型是从对文件的读写操作中引申出来的。把对资源的访问细分为读和写两种操作模式,这样可以大大增加并发效率。读写锁比互斥锁适用性更高,并行性也更高。

    需要注意的是,这里只是说并行效率比互斥高,并不是速度一定比互斥锁快,读写锁更复杂,系统开销更大。并发性好对于用户体验非常重要,假设互斥锁需要0.5秒,使用读写锁需要0.8秒,在类似学生管理系统的软件中,可能90%的操作都是查询操作。如果突然有20个查询请求,使用的是互斥锁,则最后的查询请求被满足需要10秒,估计没人接收。使用读写锁时,因为读锁能多次获得,所以20个请求中,每个请求都能在1秒左右被满足,用户体验好的多。

    二 读写锁特点

    1 如果一个线程用读锁锁定了临界区,那么其他线程也可以用读锁来进入临界区,这样可以有多个线程并行操作。这个时候如果再用写锁加锁就会发生阻塞。写锁请求阻塞后,后面继续有读锁来请求时,这些后来的读锁都将会被阻塞。这样避免读锁长期占有资源,防止写锁饥饿。

    2 如果一个线程用写锁锁住了临界区,那么其他线程无论是读锁还是写锁都会发生阻塞。

    三 读写锁使用的函数

    操作

    相关函数说明

    初始化读写锁

    pthread_rwlock_init 语法

    读取读写锁中的锁

    pthread_rwlock_rdlock 语法

    读取非阻塞读写锁中的锁

    pthread_rwlock_tryrdlock 语法

    写入读写锁中的锁

    pthread_rwlock_wrlock 语法

    写入非阻塞读写锁中的锁

    pthread_rwlock_trywrlock 语法

    解除锁定读写锁

    pthread_rwlock_unlock 语法

    销毁读写锁

    pthread_rwlock_destroy 语法

     

     

    展开全文
  • linux:读写

    千次阅读 2018-05-18 19:51:02
    ps:读写本质是一种自旋 二、为什么需要读写? 有时候,在多线程中,有一些公共数据修改的机会比较少,而读的机会却是非常多的,此公共数据的操作基本都是读,如果每次操作都给此段代码加锁,太浪费...
  • Redisson读写

    千次阅读 2020-09-17 08:35:05
    读写 一次只有一个线程可以占有模式的读写, 但是...通常, 当读写处于读模式住状态时, 如果有另外线程试图以模式加锁, 读写通常会阻塞随后的读模式请求, 这样可以避免读模式长期占用, 而等待的模式
  • 读写写锁的饥饿问题

    千次阅读 2020-02-07 17:58:07
    还有一点需要陈述,就是有些读写在读写优先级可能有所偏重(这当然也是避免饥饿的一种方法),所以在使用前我们需要清楚我们的程序需要的是什么以及库中的实现是什么,这样才能出一个响应性更优的代码. 2020年5月19...
  • 广联达(专业写锁程序)

    热门讨论 2011-07-11 17:30:04
    现在将广联达写锁程序发给你,我们的加密狗可以直接用正版新盘安装! 操作步骤: 1 安装狗驱动 2 插加密狗 3 打开rockedit输入基本密码1(8fef)和基本密码2(b08c)点确定 4 左上角点(文件),打开相关定额.RKI,点...
  • win7,win8开启Bitlocker功能后,我们可以对我们的磁盘进行加密,使用时需要先解锁才能使用,解锁后如果想立刻上锁就不是很方便了,这里介绍通过磁盘点击弹出右键菜单增加一项“上锁”功能来快速实现。 具体步骤: A...
  • MySQL读写总结

    千次阅读 2019-05-23 22:40:53
    写锁:是一种排他,一个持有写锁会阻塞其他的写锁和读,从而保证了一个只有一个事务进行操作,并且防止其他事务读取正在写入资源,避免了脏读; 策略 策略:就是在的开销和数据安全性之间寻求平衡; ...
  • ReentrantLock属于排他,这些在同一时刻只允许一个线程进行访问,而读写在同一时刻可以允许多个线程访问,但是在线程访问时,所有的读和其他线程都被阻塞。读写维护了一对,一个读和一个写锁,通过...
  • 读写ReadWriteLock的实现原理

    千次阅读 2019-11-06 10:13:17
    什么是读写 ReadWriteLock存在的意义是什么?读写适用于什么场景?什么是降级和升级?
  • 数据库读写锁详解

    千次阅读 2020-04-09 15:09:55
    读取行为发生之前,获取写锁。这意味着如果有其他尚未提交的事务已经修改了结果集,本读取模式会等待这些事务结束,以确保自己稍后可以读取到这些事务对结果集的修改。 2. 读取行为发生之后,当前事务提交之前,本...
  • 仅使用互斥实现读写

    千次阅读 2018-08-27 09:46:46
    清楚认识到读写分为共享(读)和独占写锁),可能通过...避免轮询可通过互斥+条件变量实现读写,具体实现见一篇博文。 以下是代码实现: #include&lt;pthread.h&gt; pthread_mutex_t...
  • golang中关于读写、互斥的理解

    千次阅读 2019-05-30 20:22:26
    的概述 为了解决协程同步的问题我们使用了channel,但是GO也提供了传统的同步工具。 它们都在GO的标准库代码包sync和sync/atomic中。 下面我们看一下的应用。 什么是呢?就是某个协程(线程)在访问某个...
  • C++ std::shared_mutex读写

    万次阅读 2019-08-25 12:10:25
    读写锁把对共享资源的访问者划分成读者和者,读者只对共享资源进行读访问,者则需要对共享资源进行操作。C++17开始,标准库提供了shared_mutex类(在这之前,可以使用boost的shared_mutex类或系统相关api)。...
  • golang互斥和读写性能分析

    千次阅读 2019-04-05 21:42:20
    在并发操作中为了防止多任务同时修改共享资源导致的不确定结果,我们可能会用到互斥锁和读写锁。 一:互斥锁 1.互斥锁有两种操作,获取锁和释放锁...1.读写锁有四种操作 读上锁 读解锁 写上锁 解锁 2.锁最多有一...
  • 写锁

    万次阅读 多人点赞 2019-07-03 22:23:26
    共享(S)又称读,若事务T对...排他(X)又称写锁。若事务T对数据对象A加上X,事务T可以读A也可以修改A,其他事务不能再对A加任何,直到T释放A。这保证了其他事务在T释放A之前不能再读取和修...
  • 互斥、读写 、 自旋和RCU

    万次阅读 2016-05-07 19:22:37
    互斥 mutex:在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。...如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其
  • 重入、读写实现

    千次阅读 2019-04-01 15:48:44
    重入 实现重入 重进入是指任意线程在获取到之后能够...线程再次获取需要去识别获取的线程是否为当前占据的线程,如果是,则再次成功获取。 的最终释放:线程重复n次获取了,随后在第n次释放...
  • 文章目录基于zookeeper 实现分布式读写分布式读写分布式读写思路分布式读写代码实现测试 基于zookeeper 实现分布式读写 ...读写是同一时刻可以允许多个读操作访问,但是在操作访问时,所有...
  • 通过编写文件读写及上锁的程序,进一步熟悉Linux中文件I/O相关的应用开发,并且熟练掌握open()、read()、write()、fcntl()等函数的使用。 2. 实验内容 在Linux中FIFO(先进先出)是一种进程间的管道通信...
  • 读写,为什么要用读写;

    千次阅读 2019-05-15 18:34:14
    但是,如果有一个线程想去共享资源来,就不应该在有其他线程可以对该资源进行读或; 读-读 可以共存,读- 不能共存, - 可以工作; 操作:原子+独占,整个过程必须是一个完整的,中间不许被分割,不许被打断; ...
  • 上午在看UNP卷二这一节的时候及其想睡觉,就草草了事,夜晚没有事情干,就来找找博客看看这两个到底是怎么回事吧! 参考文章:https://www.ibm.com/developerworks/cn/linux/l-cn-filelock/index.html 背景知识:...
  • 最近由于工作很忙,很长时间没有更新博客了,今天为大家带来一篇有关Redisson实现分布式的文章,好了,不多说了,直接进入主题。 1. 可重入(Reentrant Lock) Redisson的分布式可重入RLock Java对象实现了...
  • 互斥和读写的区别

    万次阅读 2016-10-09 11:45:13
     同步:是指在互斥的基础(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。...
  • QT线程同步之读写

    千次阅读 2019-03-28 21:54:42
    这个允许多个进程同时读,但是只有一个。而且读不能同时进行。...4、一个线程试图对一个加了写锁的互斥量进行上写锁,阻塞。 所以可以看出,读写比较适用的情况是:需要多次对共享的数据进行读操作的阅读线程...
  • sqlite读写和线程模式

    万次阅读 2019-06-14 18:06:40
    sqlite读写 SQLite3总共有三种事务类型:BEGIN [ DEFERRED /IMMEDIATE / EXCLUSIVE ] TRANSCATION,提供以下五种的文件状态,按锁的级别依次是:... 文件没有持有任何,即当前数据库不存在任何读或...
  • Linux中常见的主要有: 互斥、读写、自旋。这三种的使用以及区别将在下面一步步深入了解。 1 互斥 1.1 互斥的作用 互斥(也称互斥量)可以用于保护关键代码段,以确保其独占式的访问,和有点像一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 551,095
精华内容 220,438
关键字:

上锁的上怎么写