ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java锁机制

2019-05-07 13:42:59  阅读:324  来源: 互联网

标签:Java Thread System println new 机制 out blockingQueue


 

 

 公平锁:指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到;

非公平锁:指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象。

公平锁就是队列先来后到;

非公平锁就是允许加塞,默认非公平,这种效率高;

我们现在用的两种锁Lock和synchronized

        ReentrantLock lock = new ReentrantLock(true); //公平锁
/*
             ReentrantLock(boolean fair) {  //Creates an instance of ReentrantLock with the given fairness policy
                                            //fair - true if this lock should use a fair ordering policy
            sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync();
               }
*/
        ReentrantLock lock2 = new ReentrantLock(); //false 天生就是非公平锁
/*        * Creates an instance of {@code ReentrantLock}.
       * This is equivalent to using {@code ReentrantLock(false)}
        public ReentrantLock() {
            sync = new ReentrantLock.NonfairSync();
        }*/

 

可重入锁又叫递归锁

ReentrantLock/ synchronized就是一个典型的可重入锁;可重入锁最大作用就是避免死锁;

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,
在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁;
也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。

 

自旋锁 手写

//自旋,自个循环去比较
public class SinpLockDemo {
    //原子引用
    AtomicReference<Thread> atomicReference = new AtomicReference<>(); //默认初始值为null
    public void myLock(){
        Thread thread= Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in ");

        while (!atomicReference.compareAndSet(null, thread)){ //如果为null就把它变为thread自己的;true会产生死循环,把它变为false

        }
    }
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null); //解锁,如果用完了就变为null
        System.out.println(Thread.currentThread().getName() + "\t invoked myUnlock() ");

    }
/*
AA     come in //AA先进来,首次不会走while循环--false;sleep5s
BB     come in //在AAsleep时,B线程进来,CAS一直在while循环,等待AA的释放锁;多次循环调用,好处是不用阻塞,坏处如果长时间不能获得,性能会被拖慢
AA     invoked myUnlock()
BB     invoked myUnlock()

 */

    public static void main(String[] args) {
        SinpLockDemo sinpLockDemo = new SinpLockDemo();
        new Thread(() -> {
            sinpLockDemo.myLock();
            //暂停一会线程;  自己持有这把锁5s,
             try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace();}
            sinpLockDemo.myUnlock();
             },"AA").start();
         try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}

         new Thread(() -> {
            sinpLockDemo.myLock();
             try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}
            sinpLockDemo.myUnlock();
             },"BB").start();

    }
}

独占锁(写锁)/共享锁(读锁)/ 互斥锁

 

多个线程 同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。但是

如果有一个线程想去写共享资源来,就不应该再有其它线程可以对该资源进行读或写;

小总结:读 - 读能共存

    读 - 写不能共存

    写 - 写不能共存

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
小总结:读 - 读能共存
    读 - 写不能共存
    写 - 写不能共存
//写操作:原子+独占,整个过程中必须是一个完整的统一体,中间不许被分割,被打断
 */
class MyCache{ //资源类
    private volatile Map<String, Object> map = new HashMap<>();
    //private Lock lock = new ReentrantLock();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); //可重入的读写锁;

    public void put(String key, Object value){
        rwLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName() + "\t 正在写入: " + key);
            //暂停一会线程
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace();}
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写入完成");
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            rwLock.writeLock().unlock();
        }
    }
    public void get(String key){
        rwLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName() + "\t 正在读取:");
            //暂停一会线程
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace();}
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读取完成: " + result);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            rwLock.readLock().unlock();
        }
    }
/*    public void clearMap(){
        map.clear();
    }  缓存的3大操作 */
}
public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 1; i <= 5; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.put(tempInt + "", tempInt + "" );
                },String.valueOf(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.get(tempInt + "");//"tempInt" + ""
            },String.valueOf(i)).start();
        }

    }
}

#########
1     正在写入: 1
1     写入完成
2     正在写入: 2
2     写入完成
3     正在写入: 3
3     写入完成
4     正在写入: 4
4     写入完成
5     正在写入: 5
5     写入完成
1     正在读取:
2     正在读取:
3     正在读取:
4     正在读取:
5     正在读取:
3     读取完成: 3
1     读取完成: 1
2     读取完成: 2
5     读取完成: 5
4     读取完成: 4

CountDownLatch/CycliBarrier/ Semaphore    JUC包下的

CountDownLatch 火箭发射倒计时,

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.  

import java.util.concurrent.CountDownLatch;

/*
秦灭六国
一统华夏

 */
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t国被灭");
                countDownLatch.countDown();
            }, CountryEnum.forEach_CountryEnum(i).getRetMessage()).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t秦一统华夏");

        System.out.println("============");
        System.out.println(CountryEnum.ONE); //枚举中的第一张表 ONE
        System.out.println(CountryEnum.ONE.getRetCode());//第一条记录 1
        System.out.println(CountryEnum.ONE.getRetMessage()); //第一个字段 齐

    }

    private static void closeDoor() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t上完自习,离开自习室");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t************班长最后关门走人");
    /*

    1    上完自习,离开自习室
    4    上完自习,离开自习室
    3    上完自习,离开自习室
    5    上完自习,离开自习室
    2    上完自习,离开自习室
    6    上完自习,离开自习室
    main    ************班长最后关门走人
     */


    }
}

CycliBarrier集齐七颗龙珠就能召唤神龙; 做加法,与上边的刚刚相反;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        //CyclicBarrier(int parties, Runnable barrierAction)
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("*************召唤神龙");
        });
        for (int i = 1; i <= 7 ; i++) {
            final int tempInt = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t收集到第:" + tempInt + "颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

    }
}
###################
1    收集到第:1颗龙珠
4    收集到第:4颗龙珠
2    收集到第:2颗龙珠
6    收集到第:6颗龙珠
3    收集到第:3颗龙珠
5    收集到第:5颗龙珠
7    收集到第:7颗龙珠
*************召唤神龙

Semaphone 信号灯 信号亮   争车位

多个线程抢多个资源; 走一辆进一辆,一减一加,一个退一个进,可以代替syc和lock(它俩默认都是非公平锁,效率高)

主要用于两个目的:一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); //模拟3个停车位
        for (int i = 1; i <= 6 ; i++) { //模拟6辆汽车
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "抢到车位");
                     try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}
                    System.out.println(Thread.currentThread().getName() + "停车3s后离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

1抢到车位
3抢到车位
2抢到车位
2停车3s后离开车位
4抢到车位
3停车3s后离开车位
5抢到车位
1停车3s后离开车位
6抢到车位
4停车3s后离开车位
5停车3s后离开车位
6停车3s后离开车位

 队列+ 阻塞队列

当阻塞队列是空时,从队列汇总获取元素的操作将会被阻塞;

当阻塞队列是满时,往队列里添加元素的操作将会被阻塞;

Integer.MAX_VALUE 21亿

 

 

        ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        //#####################异常组################
        System.out.println(blockingQueue.add("1")); //true
        System.out.println(blockingQueue.add("2")); //true
        System.out.println(blockingQueue.add("3")); //true
        //System.out.println(blockingQueue.add("4")); //满了就会抛异常java.lang.IllegalStateException: Queue full

        //System.out.println(blockingQueue.element()); //1 元素是否为空

        System.out.println(blockingQueue.remove());//1
        System.out.println(blockingQueue.remove());//2
        System.out.println(blockingQueue.remove());//3
        //System.out.println(blockingQueue.remove());//java.util.NoSuchElementException
        //##############返回布尔值组
        System.out.println(blockingQueue.offer("a")); //true
        System.out.println(blockingQueue.offer("b")); //true
        System.out.println(blockingQueue.offer("c")); //true
        System.out.println(blockingQueue.offer("d")); //false

        System.out.println(blockingQueue.peek()); //探测最顶端的元素 a

        System.out.println(blockingQueue.poll()); //a
        System.out.println(blockingQueue.poll()); //b
        System.out.println(blockingQueue.poll()); //c
        System.out.println(blockingQueue.poll()); //null

        //##############阻塞###############
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        System.out.println("============");
        //blockingQueue.put("d"); //满了就会一直阻塞
        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();
        //blockingQueue.take();
       //#########超时控制##########
        System.out.println(blockingQueue.offer("1", 2L, TimeUnit.SECONDS)); //true
        System.out.println(blockingQueue.offer("1", 2L, TimeUnit.SECONDS)); //true
        System.out.println(blockingQueue.offer("1", 2L, TimeUnit.SECONDS)); //true
        System.out.println(blockingQueue.offer("1", 2L, TimeUnit.SECONDS)); //2s之后 false

阻塞队列之SynchronousQueue队列

 

同步队列不存储,不消费就不生产

public class SychronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
        new Thread(() -> {

            try {
                System.out.println(Thread.currentThread().getName() + "\t put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "\t put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "\t put 3");
                synchronousQueue.put("3");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"aa").start();

        new Thread(() -> {

            try {
                //暂停一会
                try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace();}
                System.out.println(Thread.currentThread().getName() + "\t" + synchronousQueue.take()); //5s一组,5s一组

                try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace();}
                System.out.println(Thread.currentThread().getName() + "\t" + synchronousQueue.take());

                try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace();}
                System.out.println(Thread.currentThread().getName() + "\t" + synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"bb").start();


    }
}


aa     put 1
bb    1
aa     put 2
bb    2
aa     put 3
bb    3

 

 线程通信之生产者和消费者传统版

  • Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects. 
高内聚低耦合前提下,线程操作资源类
判断/干活/通知
多线程编程需要注意,防止多线程的虚假唤醒,多线程的判断不可以使用if,用while

Synchronized和Lock区别:

  ①原始构成

  synchronized是关键字属于JVM层面,monitorenter(底层是通过monitor对象来完成,其实wait/ notify等方法也依赖于monitor对象只有在同步块或方法中才能调用wait/ notify等方法)

关键字
monitorenter 锁的监控器进来,底层寄存器加1;它属可重入锁
aload_1
monitorexit 出来,正常退出
monitorexit 这种是异常退出; 保证不会产生死锁或底层故障

new           #3     // class java/util/concurrent/locks/ReentrantLock  new出来的

  Lock是具体类(java.util.concurrent.Locks.Lock)是api层面的锁

  ②使用方法

  synchronized不需要用户去手动释放锁,当synchronized代码执行完之后系统会自动让线程释放对锁的占用;

  ReentrantLock则需要用户去手动释放锁,若没有主动释放锁,就有可能导致出现死锁现象;

  需要Lock( )和unlock( ) 方法配合try/ finally语句块来完成;

  ③等待是否可中断

  sychronized不可中断,除非抛出异常或者正常运行完成; ReentrantLock可中断,1.设置超时方法tryLock(long timeout,TimeUnit unit)  2.lockInterruptibly()放代码块中,调用interrupt( )方法可中断;

 ④加锁是否公平

synchronized非公平锁;ReentrantLock两者都可以,默认公平锁,构造方法可以传入boolean值,true为公平锁,false为非公平锁;

  ⑤锁绑定多个条件Condition

  synchronized没有; ReentrantLock用来实现分组唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

 

 

 

 

 

 

标签:Java,Thread,System,println,new,机制,out,blockingQueue
来源: https://www.cnblogs.com/shengyang17/p/10798634.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有