java自旋锁的实现及其分析

2023-11-13

自旋锁是指一个线程尝试获取某个锁时,如果该锁已经被其他线程占用了,就一直循环检测锁释放被释放,而不是像互斥锁一样让线程进入挂起或者睡眠状态。

自旋锁的的缺点就是会一直死循环一直到获取锁为止,这样会一直消耗cpu内存,但是与互斥锁把线程阻塞,然后再次被唤醒相比在性能方面还是有优势的,因为频繁的从用户态切到内核态,需要消耗系统资源,性能也更惨,但是目前的jvm对synchronized实现做了修改采用自旋的到一定次数或者时间就进入阻塞状态,结合了自旋和阻塞,使得目前的synchronized性能大大提高了。

非公平的重入自旋锁的实现过程:

  • 加锁:判断cas中线程是否是当前线程,如果是则计数器+1,如果不是则死循环一直到修改成功(即获取锁成功),才结束循环

  • 解锁:判断当前获取到锁的线程是否是cas中的线程,如果是则看计数器重入了多少次,如果重入了直接计数器-1,否则直接通过cas修改释放锁。

  • /**
     * @Author:苏牧夕
     * @Date:2020/3/19 23:46
     * @Version 1.0
     */
    public class AtoLock {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger();
            System.out.println(atomicInteger.get());
            ThreadPoolExecutor poolExecutor = ThreadPool.InstancePool.poolExecutor;
            SpilLock spilLock = new SpilLock();
    //        FairSpilLock spilLock = new FairSpilLock();
            for (int i = 0; i < 5; i++) {
                int x = i;
               poolExecutor.execute(
                        () -> {
                            spilLock.lock();
                            spilLock.lock();
                            try {
                                System.out.println(Thread.currentThread().getName() + x + ",各种骚操作");
                                TimeUnit.SECONDS.sleep(3);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            spilLock.unlock();
                            spilLock.unlock();
                        }
                );
            }
    //        System.out.println("操作了几次:" + atomicInteger.get());
            poolExecutor.shutdown();
        }
        static class SpilLock implements Lock {
            private static AtomicReference<Thread> cas = new AtomicReference<>();
            private static AtomicInteger count = new AtomicInteger(0);
    
            @Override
            public void lock() {
                Thread thread = Thread.currentThread();
                if (thread==cas.get()){
                    count.incrementAndGet();
                    System.out.println(Thread.currentThread().getName()+"上锁成功");
                    return;
                }
                while (!cas.compareAndSet(null, thread)) {
                }
                System.out.println(Thread.currentThread().getName()+"上锁成功");
    
            }
    
            @Override
            public void lockInterruptibly() throws InterruptedException {
    
            }
    
            @Override
            public boolean tryLock() {
                return false;
            }
    
            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                Thread thread = Thread.currentThread();
                Long timeout= unit.toNanos(time);
                if (cas.get()==thread){
                    count.incrementAndGet();
                    return true;
                }
                Long start = System.nanoTime();
                while (!cas.compareAndSet(null,thread)){
                    if (thread.isInterrupted()){
                        thread.interrupt();
                        throw  new InterruptedException();
                    }
                    long tt=System.nanoTime()-start;
                    if (tt>=timeout){
                        return false;
                    }
                }
                return true;
            }
    
            public  void unlock() {
                Thread thread = Thread.currentThread();
                if (thread==cas.get()){
                    if (count.get()>0){
                System.out.println(Thread.currentThread().getName()+"解锁成功");
                        count.decrementAndGet();
                    }else{
                        if (cas.compareAndSet(thread, null)) {
                System.out.println(Thread.currentThread().getName()+"解锁成功");
                        }
                    }
                }
            }
    
            @Override
            public Condition newCondition() {
                return null;
            }
        }
    }
    
    
  • 公平自旋锁

    逻辑:

    • 加锁:一个标记记录当前获得锁的编号currNo,然后给每条线程匹配一个编号,如果当前锁编号和当前线程的编号匹配则解锁自旋
    • 解锁:通过cas修改 锁的编号currNo,改为当前获得锁的线程的编号+1
    
    static class FairSpilLock implements Lock{
       AtomicInteger currNo = new AtomicInteger(0);
       AtomicInteger threadNo = new AtomicInteger(0);
       ThreadLocal<Integer> local = new ThreadLocal<>();
       AtomicInteger count = new AtomicInteger(0);
           @Override
           public void lock() {
           //生成一个线程编号
               int thNo = threadNo.getAndIncrement();
               //把编号和线程联系起来
               local.set(thNo);
               //自旋当前锁的编号curr释放和当前线程编号一样,如果一样则结束自旋
               while (thNo!=currNo.get()){
                   Thread.yield();//如果不是则当前线程礼让,可以不加,加了减少循环次数
               }
               System.out.println(Thread.currentThread().getName()+"加锁成功");
    
           }
    
           @Override
           public void lockInterruptibly() throws InterruptedException {
    
           }
    
           @Override
           public boolean tryLock() {
               return false;
           }
    
           @Override
           public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
               return false;
           }
    
           @Override
           public void unlock() {
           //获取当前线程的编号
               Integer thNo = local.get();
               //cas修改当前锁编号
               if (currNo.compareAndSet(thNo,thNo+1)){
                   System.out.println(Thread.currentThread().getName()+"解锁成功!");
               }
    
           }
    
           @Override
           public Condition newCondition() {
               return null;
           }
       }
    

    优点:实现了公平性,使得每条线程都有机会执行

    缺点:每条线/进程都占用处理器在读写标记变量,导致系统性能降低

  • CLH公平自旋锁

    来解决频繁读写标记,就出现了CLH自旋锁算法

    首先简单说一下ThreadLocal这个类的作用,该类负责提供线程都有的本地变量(局部变量),在该类存储的变量,在整个线程存活的过程中可以使用,但是其他线程是无法取的其他线程的局部变量的,类似于一每条线程都有一个私有的Map存储参数,通常使用该类来存储线程的上下文或者id,又因为变量都是线程内部的所以不存在并发问题,所以是线程安全的。

    CLH算法则是利用了ThreadLocal来存储每条线程的节点和它前一条线程的节点作为前继节点,每条线程只自旋判断前继节点的状态,如果前继节点释放锁状态改变,则本线程获取到锁,整个过程在模拟队列,但是该队列是非真实存在的,只是逻辑上是队列,也正是因为通过模拟队列来每条线程有序执行,从而达到公平。

    那么多个线程直接是如何链接起来的呢,首先每条线程都有一个节点用于保存自身的状态是否加锁或者解锁状态,并且存储在ThreahLocal,同时也存储来前一条线程的节点作为,该线程前继节点,那么每条线程是如何获取前一个线程的节点作为前继节点的呢,这里我们需要使用原子引用类来存储尾节点,每条线程创建的时候通过cas操作,把自身作为尾节点,把前一个尾节点作为其前继节点,也就是做,每次有线程申请锁就获取保存了前一条线程节点的尾节点作为其前继节点,这样就把多条线程链接起来,而且还是有序的。

    此外,ThreadLocal容易出现内存泄漏,为什么会出现内存泄漏呢?因为该类内部使用Map存储,而且key继承软引用类,然后value映射起来的,只有内存不足,gc进行垃圾回收就会把软引用回收,也就是key回收了,但是value还在,而且是无法获取的,因为key已经被回收了。

    CLH锁逻辑上是链表形式,所以同样具有很好的扩展性,性能也比较高。

    public class AtoLock {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger();
            System.out.println(atomicInteger.get());
            ThreadPoolExecutor poolExecutor = ThreadPool.InstancePool.poolExecutor;
    //        SpilLock spilLock = new SpilLock();
    //        FairSpilLock spilLock = new FairSpilLock();
            CLHSpilLock spilLock = new CLHSpilLock();
            for (int i = 0; i < 5; i++) {
                int x = i;
               poolExecutor.execute(
                        () -> {
                            spilLock.lock();
    //                        spilLock.lock();
                            try {
                                System.out.println(Thread.currentThread().getName() + x + ",各种骚操作");
                                TimeUnit.SECONDS.sleep(3);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
    //                        spilLock.unlock();
                            spilLock.unlock();
                        }
                );
            }
    //        System.out.println("操作了几次:" + atomicInteger.get());
            poolExecutor.shutdown();
        }
        static class CLHSpilLock implements Lock{
            CLHSpilLock() {
                tail = new AtomicReference<Node>(new Node());
                //init方法有延迟加载的作用
                prev = new ThreadLocal<Node>(){
                    @Override
                    protected Node initialValue() {
                        return null;
                    }
                };
                currNode = new ThreadLocal<Node>(){
                    @Override
                    protected Node initialValue() {
                        return new Node();
                    }
                };
            }
    
            static class Node{
                //用于判断前继节点释放释放锁。
                private volatile boolean locked;
            }
    
            //
           private final ThreadLocal<Node>prev;
            //线程的本地变量(局部变量),存储自身节点的状态
           private final ThreadLocal<Node>currNode;
           //存储尾节点(也是前一条线程的节点,后一条线程通过这个tail获取前一条线程变量的内存地址并且设置尾前继节点)
           private final  AtomicReference<Node> tail ;
    
    
            @Override
            public void lock() {
                //ThreahLocal存储的本地变量(局部变量),只有线程自己才可以访问到,其他的线程获取不到
                //相当于是线程的私有内存
                //获取当前线程的节点标记。
              final   Node node = currNode.get();
              //设置为当等锁的自旋状态
                node.locked=true;
                //通过cas为节点设置为当前变量,并且获取之前的尾节点作为前继节点
                //这一步是线程之间执行顺序连接起来,preNode是前一条线程的节点,
                Node predNode = tail.getAndSet(node);
                prev.set(predNode);
                //注意CLH,通过把前继节点和自身的节点存存储在ThreadLocal中,
                // 每条线程只根据自己的前继节点释放锁来判断自己释放获取到锁。
                //自旋当前线程节点的前继节点释放已经释放锁,如果释放锁则停止自旋。
                while (predNode.locked){
                }
                System.out.println(Thread.currentThread().getName()+"加锁成功");
            }
    
            @Override
            public void lockInterruptibly() throws InterruptedException {
    
            }
    
            @Override
            public boolean tryLock() {
                return false;
            }
    
            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }
    
            @Override
            public void unlock() {
                //获取线程的私有节点,然后修改状态为false表示释放锁,因为locked使用了volavte修饰,
                //当前节点的状态是下一条线程自旋判断前继节点的状态,因此修改为false时它里面可以发现状态改变,
                // volatilt保证变量在线程之间的可进行
               final Node node = currNode.get();
                node.locked=false;
                //然后把当前节点从本地变量(局部变量)(线程本地内存,反正就是只有自己可以看到的变量)中移除防止内存溢出
                currNode.remove();
                System.out.println(Thread.currentThread().getName()+"释放锁成功");
            }
    
    
            @Override
            public Condition newCondition() {
                return null;
            }
        }
      }
    

    总结:CLH是逻辑上的队列(链表),并不是真正的队列,而且自旋是自旋前继节点,根据前继节点来判断是否获得锁。

  • MCS公平自旋锁

    MCS算法和CLH算法思路非常相似,但是而且最大的区别是,MCS自旋的是自身节点,而不是前继节点,同时MCS是真正的链表,

    算法思想:每条线程都有一个自身的节点Node(Node存储着线程当前锁的状态和后继节点),存储在线程的局部变量ThreadLocal中,通过一个原子类存储上一次最新申请获取锁的线程的节点称为尾节点,并且后续线程通过cas,获取上一次最后的节点,并且把当前线程节点设置为尾节点,然后把上一次尾节点的后继节点设置为当前节点,然后当前节点进行自旋,那么自旋是如何解锁的呢,这通过解锁过程中,把当前线程节点的后继节点的状态修改为获得锁状态(每次修改后继节点的状态和链表保证了线程执行的有序性)。

    /**
     * @Author:苏牧夕
     * @Date:2020/3/19 23:46
     * @Version 1.0
     */
    public class AtoLock {
        public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger();
            System.out.println(atomicInteger.get());
            ThreadPoolExecutor poolExecutor = ThreadPool.InstancePool.poolExecutor;
    //        SpilLock spilLock = new SpilLock();
    //        FairSpilLock spilLock = new FairSpilLock();
    //        CLHSpilLock spilLock = new CLHSpilLock();
            MCSSpilLock spilLock = new MCSSpilLock();
            for (int i = 0; i < 5; i++) {
                int x = i;
                poolExecutor.execute(
                        () -> {
                            spilLock.lock();
    //                        spilLock.lock();
                            try {
                                System.out.println(Thread.currentThread().getName() + x + ",各种骚操作");
                                TimeUnit.SECONDS.sleep(3);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
    //                        spilLock.unlock();
                            spilLock.unlock();
                        }
                );
            }
    //        System.out.println("操作了几次:" + atomicInteger.get());
            poolExecutor.shutdown();
        }
    
        static class MCSSpilLock implements Lock {
            private final ThreadLocal<Node> curnode;
            private AtomicReference<Node> tail;
            public MCSSpilLock() {
                //第一条线程直接初始化当前节点和尾节点
                this.curnode = new ThreadLocal<Node>() {
                    @Override
                    protected Node initialValue() {
                        return new Node();
                    }
                };
                tail = new AtomicReference<Node>();
            }
    
            @Override
            public void lock() {
                //获取当前线程局部变量是否已经存在node节点
                Node currNode = curnode.get();
                //线程的节点为null,则创建一个节点
                if (currNode == null) {
                    currNode = new Node();
                }
                //然后通过尾插法,先把尾节点取处理,并把当前节点设为尾节点。
                Node predNode = tail.getAndSet(currNode);
                if (predNode != null) {
                    //如果不是第一个节点则设置为true,处于等待锁状态
                    currNode.locked = true;
                    //把前一条线程的节点后继节点设置尾当前节点,保持公平
                    predNode.next = currNode;
                }
                //轮询当前节点
                while (currNode.locked) {}
                System.out.println(Thread.currentThread().getName()+"上锁成功");
            }
    
            @Override
            public void lockInterruptibly() throws InterruptedException {
    
            }
    
            @Override
            public boolean tryLock() {
                return false;
            }
    
            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }
    
            @Override
            public void unlock() {
                //获取当前线程的节点
                Node node = curnode.get();
                //如果是最后一个节点则把tail也进行回收。
                if (node.next == null) {
                    tail.compareAndSet(node, null);
                }
                //如果有后继线程,则修改状态为获取锁状态。并且把当前节点的引用设置为null
                if (node.next != null) {
                    node.next.locked = false;
                    node.next = null;
                    //回收当前线程的节点。防止内存泄漏
                    curnode.remove();
                }
                System.out.println(Thread.currentThread().getName()+"解锁成功");
            }
    
            @Override
            public Condition newCondition() {
                return null;
            }
    
            static class Node {
                //false表示获得锁。
                private volatile boolean locked = false;
                private Node next;
            }
        }
    
    

    参考CLH和MCS

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java自旋锁的实现及其分析 的相关文章

  • 是否可以使用检测重新定义核心 JDK 类?

    我想重新定义字节码StackOverflowError构造函数 因此当堆栈溢出发生时我有一个 钩子 我想要做的就是在构造函数的开头插入对我选择的静态方法的单个方法调用 是否有可能做到这一点 您应该能够使用两种方法之一来完成此操作 除非在过去
  • 如何在不改变的情况下将字符串转换为字节?

    我需要一个解决方案将字符串转换为字节数组而不需要像这样进行更改 Input String s Test Output String s Test byte b Test 当我使用 s getBytes 那么回复是 B 428b76b8 但我
  • Spring Rest POST Json RequestBody 不支持内容类型

    当我尝试使用 post 方法发布新对象时 RequestBody 无法识别 contentType Spring 已经配置完毕 POST 可以与其他对象一起使用 但不能与这个特定对象一起使用 org springframework web
  • 为什么Java HashMap的最大容量是1<<30而不是1<<31?

    Why is the maximum capacity of a Java HashMap 1 lt lt 30 and not 1 lt lt 31 even though the max value of an int is 231 1
  • JPA 为每个项目选择最新实例

    假设我有一个会议实体 每次会议都有一个与会者和一个会议日期 在我的会议表中 我可能为每个与会者举行多个会议 每个会议都有不同的日期 我需要一个 JPA 查询 该查询将为所有与会者仅选择最新的会议 例如 如果我的桌子看起来像这样 Meetin
  • 如何使用 Gradle 2.10 将 ANTLR 词法分析器语法导入到另一个语法中?

    我一直在和 Terence Parr 一起学习 ANTLR 4权威的 ANTLR 4 参考 到目前为止我一直在使用 Gradle 2 10 及其内置 ANTLR 插件进行跟踪 然而 我在获取一些我从第 4 章第 38 41 页改编的代码以使
  • UnsupportedOperationException:特权进程中不允许使用 WebView

    我在用android sharedUserId android uid system 在我的清单中获得一些不可避免的权利 从 HDMI 输入读取安卓盒子 http eweat manufacturer globalsources com s
  • splitByWholeSeparatorPreserveAllTokens 和 split 之间的区别

    有什么区别StringUtils splitByWholeSeparatorPreserveAllTokens and String split With splitByWholeSeparatorPreserveAllTokens 我们可
  • 为什么这不会导致 NullPointerException?

    public class Null public static void greet System out println Hello world public static void main String args Null null
  • JavaPreparedStatementUTF-8字符问题

    我有一份准备好的声明 PreparedStatement st 在我的代码中 我尝试使用 st setString 方法 st setString 1 userName userName 的值为 ak a setString 方法将 ak
  • MAC OS 的 java.awt.Robot 类中出现无头环境错误

    我正在尝试使用 JavaFX 应用程序捕获屏幕截图Robot class 这是我在我的应用程序中使用的代码 Rectangle screenBounds new Rectangle Screen getPrimary getBounds g
  • 创建一个 int 类型的随机数组。爪哇

    我需要创建一个随机的 int 数组 并按我自己的类对其进行排序 这是我制作数组的地方 public class MyProgram9 public static void main String args int list new int
  • 使用java读取Excel工作表的单列

    我有一张 Excel 表格 我想编写一个方法 该方法将参数作为要读取的列号 并返回一个由该列中的所有数据组成的数组 然后将该列元素放置在 xml 工作表中 我怎样才能编写一个方法来做到这一点 使用 Apache POI 您可以在他们的使用页
  • 捕获 XSS(跨站脚本)攻击的最佳正则表达式(Java 中)?

    杰夫实际上在净化 HTML http refactormycode com codes 333 sanitize html 但他的示例是用 C 编写的 而我实际上对 Java 版本更感兴趣 有人有更好的 Java 版本吗 他的示例是否足以直
  • 我们可以用java定制一个垃圾收集器吗?

    我们知道java的垃圾收集器是一个低优先级线程 在java中我们可以创建任何具有高优先级的线程 那么是否有可能拥有我们自己定制的具有可变优先级的垃圾收集器线程 我们可以根据内存管理的级别进行设置 有人尝试过吗 如果是的话 您能分享一些关于如
  • Java 8 Stream - 为什么过滤器方法不执行? [复制]

    这个问题在这里已经有答案了 我正在学习使用java流进行过滤 但是过滤后的流没有打印任何内容 我认为过滤器方法没有被执行 我的过滤代码如下 Stream of d2 a2 b1 b3 c filter s gt s startsWith b
  • Java中如何限制文件大小

    我正在我的应用程序中创建一个文件 并继续向该文件中写入一些内容 但是当我的文件达到一定大小 比如说 100 行 后 我想删除第一行并将新行写入底部 要求是我的文件应该受到限制 但它应该保留我写入文件的最新内容 请告诉我在Java中是否可行
  • 如何在jpa中共享EntityManagerFactory

    我是 jpa 的新手 这是场景 我正在开发一个 Web 应用程序 其中 多个用户可以登录 当 user1 注销时 我正在使用下面的代码 public static void closeEntityManagerFactory if enti
  • Admob - 没有广告可显示

    你好 我尝试制作一些在 Android 手机上显示广告的示例程序 并尝试在 v2 2 的模拟器上测试它 代码中的一切似乎都很好 但调试器中的 AdListener 表示 响应消息为零或空 onFailedToReceiveAd 没有广告可显
  • Java applet 是否会违反同源策略

    我需要请求一些东西并从其他域获取信息 我知道由于同源政策 javascript 无法做到这一点 我的另一个选择是通过我的服务器发出代理请求 我不希望请求来自我的服务器的 IP 也不想为我的服务器创建额外的负载 并且希望客户端这样做 是否可以

随机推荐

  • Python最强学习知识点:面相对象基础语法

    面相对象基础语法 目标 dir 内置函数 定义简单的类 只包含方法 方法中的 self 参数 初始化方法 内置方法和属性 01 dir 内置函数 知道 在 Python 中 对象几乎是无所不在的 我们之前学习的 变量 数据 函数 都是对象
  • 单周期RISC-V架构CPU的设计---设计篇

    目录 一 模块设计 1 pc reg v 1 1 功能说明 1 2 整体框图 1 3 接口列表 1 4 内部信号说明 1 5 关键电路 2 id v 2 1 功能说明 2 2 整体框图 2 3 接口列表 2 4 内部信号说明 2 5 关键电
  • Vim语法检查插件cppSyntaxCheck

    下载cppSyntaxCheck master https github com sjp 1024 Classroom notes blob master cppSyntaxCheck master zip 将解压文件中的cppSyntax
  • 40+野路子学习软件编程记录

    1 软件几乎零基础 机械专业 手机行业项目管理工作 2 疫情期间 闲来无事 突发兴趣 3 有点开窍入门 4 树莓派Linux学习入手 openwrt homeassiant airplay和媒体局域网服务等 5 学习Python读ds18b
  • postgresql导出表结构

    pg dump命令可以导出数据库中的表结构 s 选项用来只导出表结构 而不会导出表中的数据 t 选项用来指定要导出的数据库表 pg dump s t tlb exampledb gt tmp tlb exampledb是数据库 tlb是ex
  • MySQL对大小写敏感吗

    见字如面 见标题知内容 你有遇到过因为MYSQL对大小写敏感而被坑的体验吗 之前看过阿里巴巴Java开发手册 在MySql建表规约里有看到 强制 表名 字段名必须使用小写字母或数字 禁止出现数字开头 禁止两个下划线中间只 出现数字 数据库字
  • Python代码:根据txt文件批量提取图片

    个人微信公众号 AI研习图书馆 欢迎关注 深度学习知识及资源分享 学习交流 共同进步 1 介绍 Python代码 根据txt文件批量提取图片并保存至另一文件夹 用于深度学习 图片数据预处理 2 Python代码 实现方案一 import s
  • vue点击按钮跳转新页面

    const str location href split 0 window open str router的name
  • java自动登录 selenium 自动登录并获取cookie

    选择操作网页 我用的edge 谷歌我的版本太高没有对应的驱动 下载Edge的驱动程序 直接解压就好里面只有一个 exe文件 https developer microsoft com en us microsoft edge tools w
  • bluez调试笔记

    蓝牙系列 bluez调试笔记 weixin 41069709的博客 CSDN博客 bluezbluez移植https blog csdn net weixin 41069709 article details 125168114 spm 1
  • 指针与const限定符

    const限定符和指针结合起来常见的情况有以下几种 const int a int const a 这两种写法是一样的 a是一个指向const int的指针 a所指向的内存单元不可改写 所以 a 是不允许的 但a可以改写 所以a 是允许的
  • 低电压电池充不进电问题分析

    作者 AirCity 2020 3 1 Aircity007 sina com 本文所有权归作者Aircity所有 1 问题现象 某MSM8998手机项目 老化测试一端时间后 有很小比例的机器关机黑屏 充电1h没有反应 测量电池电压 2 6
  • Java实验二 货物进销管理系统【简单易懂】

    写在前面 这个题目主要通过仔细阅读题目的需求 再通过对文件流以及Vector ArrayList等容器的控制来完成整个代码体系的构建 因为当时正在学习 对代码的构建逻辑不够规范 思想不够到位的地方也难以避免 在这里抱歉抱歉 不过这一版的构建
  • 在K2P路由器,非官方openwrt固件,安装软件遇到的坑!

    手上有一台斐讯K2P A2版本的路由器 一直空闲在宿舍 最近发现这个路由器被破解了 可以刷上不死breed 还有很多大神做的固件 因此我想用它刷上openwrt系统后 安装某软件 从而直接在路由器上完成学校的宽带验证登陆 接下来我就介绍我在
  • 04 Cesium—Cesium ion介绍

    文章中所有操作均是在 Cesium 1 91 版本下进行的 其它版本差异请自行适配 Cesium ion Cesium ion 是一个提供瓦片图和3D地理空间数据的平台 Cesium ion 支持把数据添加到用户自己的 CesiumJS 应
  • 8421BCD码 5421BCD码 余三码 格雷码 余三循环码之间的关系,转换以及简易方法

    8421BCD码 5421BCD码 余三码 格雷码 余三循环码之间的关系 转换以及简易方法 1 有权码和无权码的包括 2 各种码值的介绍 8421码的简介 8421码又称为BCD码 是十进代码中最常用的一种 在这种编码方式中 每一位二值代码
  • java设计模式之单例模式

    目录 一 单例模式 二 饿汉模式和懒汉模式 1 饿汉模式 线程安全 2 懒汉模式
  • KVM无法进入virt-manager,提示Unable to init server: Could not connect: Connection refused

    1 KVM virt manager不能以root用户进入 需切换成普通用户或者sudo用户 2 需要配置ssh 密钥 3 需要安装Xming或者Xmanager等KVM可用等图形界面软件 无法连接kvm 设置用户到组 一定要当前用户不要r
  • 应用安全系列之九:HTTP参数污染

    本系列文章主旨在于介绍一些漏洞类型产生的基本原理 探索最基础的解决问题的措施 不排除有些语言或者系统提供的安全的API可以更好地更直接地解决问题 也不排除可以严格地输入验证来解决 URL参数注入 也称为HPP HTTP Parameter
  • java自旋锁的实现及其分析

    自旋锁是指一个线程尝试获取某个锁时 如果该锁已经被其他线程占用了 就一直循环检测锁释放被释放 而不是像互斥锁一样让线程进入挂起或者睡眠状态 自旋锁的的缺点就是会一直死循环一直到获取锁为止 这样会一直消耗cpu内存 但是与互斥锁把线程阻塞 然