首页 文章 精选 留言 我的

精选列表

搜索[编译原理],共10000篇文章
优秀的个人博客,低调大师

浅谈Java多线程与并发原理

前序 线程安全问题的主要诱因 存在共享数据(也称临界资源) 存在多条线程共同操作这些共享数据 解决方法:同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作 互斥锁的特征 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问。互斥性也称为操作的原子性。可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应该获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致。注:synchronized 锁的不是代码,锁的是对象 获取锁的分类:获取对象锁、获取类锁 获取对象锁的两种用法: 同步代码块(synchronized(this),s

优秀的个人博客,低调大师

区块链的原理与应用? -2

接着上一篇.上一篇还没有输出,其实加入了index在实体类中. package com.sha256.sha256.bean; import com.sha256.sha256.utils.SHA256Util; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; @Data @NoArgsConstructor @AllArgsConstructor public class Block { private long index; private String hash; // our signature private String previousHash; // the hash of previous block private String data; //our data will be a simple message. private long timeStamp; //as number of milliseconds since 1/1/1970. //Block Constructor public Block(long index,String data,String previousHash){ this.index = index; this.data = data; this.previousHash = previousHash; this.timeStamp = new Date().getTime(); this.hash = SHA256Util.calculateHash(this); //Making sure we do this after we set the other values. } } 测试类: package com.sha256.sha256.test; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.gson.GsonBuilder; import com.sha256.sha256.bean.Block; import com.sha256.sha256.utils.SHA256Util; import java.util.ArrayList; import java.util.Random; public class TestSHA256 { //声明一个区块链,用于添加Block实体 public static ArrayList<Block> blockChain = new ArrayList<>(); public static void main(String[] args) { //test1 测试三个被加密字符串 加密后的hash值的差别 /** * 虽然第三条信息仅仅多一个".",但加密后的数据hash相差极大 */ // String message0 = "我是要被加密的信息"; // String message1 = "我是要被加密的信息"; // String message2 = "我是要被加密的信息."; // String encryptionMessage0 = SHA256Util.applySha256(message0); // String encryptionMessage1 = SHA256Util.applySha256(message1); // String encryptionMessage2 = SHA256Util.applySha256(message2); // System.out.println(encryptionMessage0); // System.out.println(encryptionMessage1); // System.out.println(encryptionMessage2); // // //test2 创建区块链逻辑, 因为第一个块没有上一个块的hash头部值,所以输入0 作为前一个块的previous hash // /** // * 由于在{@link SHA256Util#calculateHash(Block)} // * 中对同时产生的new Date().getTime() (timestamp) // * 也加入进行了hash加密,所以固有的message (data)及 // * previoushash之和进行了加密. // */ // Block genesisBlock = new Block(0,"这是第一个区块中的要被加密的信息和交易信息","0"); // String hash1 = genesisBlock.getHash(); // System.out.println("Hash for block 1 : "+hash1); // // Block secondBlock = new Block(1,"这是第二个区块,以及其中信息!!!它的前区块头部hash我们拿上一个的来使用",hash1); // String hash2 = secondBlock.getHash(); // // System.out.println("Hash for block 2 : "+hash2); // // Block thirdBlock = new Block(2,"这是第三个区块,它的hash应该已经被前两个的信息纳入进来了,它的hash如果对不上,那么说明前面的信息被改动过了",hash2); // String hash3 = thirdBlock.getHash(); // System.out.println("Hash for block 3 : "+hash3); // //// test3 add our blocks to the blockchain ArrayList : // blockChain.add(new Block(0,"区块链上第一小节","0")); // blockChain.add(new Block(1,"区块链第二小节",blockChain.get(blockChain.size()-1).getHash())); // blockChain.add(new Block(2,"区块链第三小节",blockChain.get(blockChain.size()-1).getHash())); int chainNumber = 24; int index = 0; while (chainNumber > 0) { System.out.println("blockChain.size():" + blockChain.size()); if (blockChain.size() == 0) { blockChain.add(new Block(0, "创世块", "0")); } index++; blockChain.add(new Block(index, "区块内容" + blockChainMessage(index), blockChain.get(blockChain.size() - 1).getHash())); chainNumber--; } // JSONArray blockChainJson1 = (JSONArray)JSONArray.toJSON(blockChain); //JSONArray是不排版的 // System.out.println(blockChainJson1); String blockChainJson = new GsonBuilder().setPrettyPrinting().create().toJson(blockChain); System.out.println(blockChainJson); } //模拟一些交易信息 private static String blockChainMessage(int getNumber) { Random random = new Random(getNumber); long l = random.nextLong(); System.out.println("blockChainMessage:" + l); return String.valueOf(l); } }

优秀的个人博客,低调大师

深入剖析线程同步工具CountDownLatch原理

0 CountDownLatch的作用 CountDownLatch作为一个多线程间的同步工具,它允许一个或多个线程等待其他线程(可以是多个)完成工作后,再恢复执行。 就像下面这样: 1 从一个Demo说起 我们直接拿源码中给出的Demo看一下,源码中的这个demo可以看做模拟一个赛跑的场景。 赛跑肯定有跑得快的运动员也有跑的慢的运动员,每个运动员就表示一个线程。 运动员听到枪声后开始起跑,而最后一个运动员到达终点后,标志的比赛的结束。 整个过程如下图所示: CountDownLatch跑步模拟 源码如下所示 public class Race { private static final int N = 4; public static void main(String[] args) throws InterruptedException { CountDownLatch startSignal = new CountDownLatch(1); // 鸣枪开始信号 CountDownLatch doneSignal = new CountDownLatch(N); // 等待N个运动员都跑完后,比赛结束(结束信号) for (int i = 0; i < N; ++i) // N个运动员准备就绪,等待枪声 new Thread(new Runner(startSignal, doneSignal, i)).start(); Thread.sleep(1000); // 等待所有运动员就绪 System.out.println("所有运动员就绪"); startSignal.countDown(); // 鸣枪,开赛 System.out.println("比赛进行中..."); doneSignal.await(); // 等待N个运动员全部跑完(等待doneSignal变为0) System.out.println("比赛结束"); } } class Runner implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; private int number; Runner(CountDownLatch startSignal, CountDownLatch doneSignal, int number) { this.startSignal = startSignal; this.doneSignal = doneSignal; this.number = number; } public void run() { try { // 等待枪声(等待开始信号startSignal变为0) System.out.println(number + "号运动员准备就绪"); startSignal.await(); // 赛跑 System.out.println(number + "号运动员跑步中..."); Thread.sleep(new Random().nextInt(10) * 1000); // 此运动员跑到终点 System.out.println(number + "号运动员到达终点"); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } } 上面代码运行后,输出如下: 0号运动员准备就绪 3号运动员准备就绪 2号运动员准备就绪 1号运动员准备就绪 所有运动员就绪 比赛进行中... 0号运动员跑步中... 1号运动员跑步中... 2号运动员跑步中... 3号运动员跑步中... 2号运动员到达终点 1号运动员到达终点 0号运动员到达终点 3号运动员到达终点 比赛结束 下面,深入到代码细节,看一下CountDownLatch初始化、countDown方法、await方法是如何实现的。 2 CountDownLatch类图 通过下图来了解一下CountDownLatch的类继承关系 CountDownLatch类图 3 CountDownLatch的初始化 CountDownLatch只有一个构造方法: public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } 他会初始化一个Sync,这是他的一个内部类,类似于ReentrantLock,Sync也继承于AbstractQueuedSynchronizer(AQS)。 AQS是个啥?可以参考笔者的另一篇文章:Java队列同步器(AQS)到底是怎么一回事 然后看一下Sync的源码 private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; // 调用AQS的setState方法,将state赋值为count的值 Sync(int count) { setState(count); } // 获取AQS state的当前值 int getCount() { return getState(); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } } } 所以CountDownLatch的初始化,其实是将参数count的值赋值给AQS的state,依然是用state来控制同步状态。 4 await方法的实现依然用上面赛跑的例子来说明这个问题。这里我们只考虑所有运动员等待枪声的情景。 回忆一下,赛跑的例子中,通过下面的方式创建了鸣枪信号: CountDownLatch startSignal = new CountDownLatch(1); // 鸣枪开始信号 然后创建了N个线程(表示N个运动员),并调用其start方法让其开始执行(运动员准备就绪,等待鸣枪开跑)。 然后通过在run方法中调用startSignal.await(),来实现等待鸣枪的动作(其实就是等startSignal的值降为0)。 我们来看一下他是怎么await的。 public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } 调用了AQS的acquireSharedInterruptibly方法 public final void acquireSharedInterruptibly(int arg) throws InterruptedException { // 判断线程是否已经被中断 if (Thread.interrupted()) throw new InterruptedException(); // 调用CountDownLatch.Sync的tryAcquireShared方法 // 此方法判断count的值是否==0,如果==0,返回1,否则返回-1 // 目前我们还没有执行countDown,所以count肯定不等于0,这里肯定返回-1 // 所以会执行到AQS的doAcquireSharedInterruptibly方法中 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } AQS.doAcquireSharedInterruptibly的实现如下 / * Acquires in shared interruptible mode. * @param arg the acquire argument */ // 此方法会在count>0时将当前线程加入到等待队列中 // 由于我们目前还没有执行countDown,所以count会保持>0 // 启动的N个线程会全部加入到队列中 private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { // 将当前线程添加到等待队列中(SHARED模式) final Node node = addWaiter(Node.SHARED); boolean failed = true; try { // 自旋获取同步状态 for (;;) { final Node p = node.predecessor(); if (p == head) { // 依然调用CountDownLatch.Sync的tryAcquireShared方法判断 // 如果count降为0,退出自旋 int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } // 将node的waitStatus设置为-1(常量SIGNAL,表示需要唤醒),并阻塞 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }** ```** 假设N=4,那么4个线程全部start后,会全部加入到队列中自旋等待,像下面这样: CountDownLatch.await自旋等待 5 countDown方法的实现 countDown方法实际上就是将AQS中的state的值-1。然后判断当前state的值是否==0,如果等于0,说明所有线程都执行结束了,需要唤醒所有等待的线程。 依然继续上面的场景,鸣枪后,所有的运动员开跑。 鸣枪这个动作实际上就是在主线程中执行: startSignal.countDown(); 这就相当于向刚才队列中的所有线程发了一个恢复执行的信号,所有线程会被唤醒,继续执行await后面的代码。 countDown具体干了啥呢? public void countDown() {sync.releaseShared(1);} 他会调用AQS的releaseShared方法 public final boolean releaseShared(int arg) {// 调用CountDownLatch.Sync的tryReleaseShared方法// 该方法尝试将count值-1,并判断-1后的count是否==0,如果==0,返回true,否则false// 该方法的源码已经在Sync的源码中给出,可翻阅上文查看if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;} 由于startSignal中count的初始值==1,startSignal.countDown()后,count变为0。所以tryReleaseShared会返回true。 然后开始执行doReleaseShared,唤醒队列中的线程。 doReleaseShared是AQS中的方法。 /** Release action for shared mode -- signals successor and ensures propagation. (Note: For exclusive mode, release just amounts to calling unparkSuccessor of head if it needs signal.)/private void doReleaseShared() {/ Ensure that a release propagates, even if there are other in-progress acquires/releases. This proceeds in the usual way of trying to unparkSuccessor of head if it needs signal. But if it does not, status is set to PROPAGATE to ensure that upon release, propagation continues. Additionally, we must loop in case a new node is added while we are doing this. Also, unlike other uses of unparkSuccessor, we need to know if CAS to reset status fails, if so rechecking.*/for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;// Node.SIGNAL == -1// 由上文可知,进入队列的线程的waitStatus都等于-1// 所以这里为trueif (ws == Node.SIGNAL) {// 尝试将waitStatus从-1改为0,如果修改成功,就恢复这个线程的执行状态if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue; // loop to recheck casesunparkSuccessor(h);}else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue; // loop on failed CAS}if (h == head) // loop if head changedbreak;}} 这里,被阻塞的线程又恢复执行,恢复到哪了呢?就是刚才自旋等待的那里。 把上面的源码直接拿下来,再说明一下(注释部分) private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { // 将当前线程添加到等待队列中(SHARED模式) final Node node = addWaiter(Node.SHARED); boolean failed = true; try { // 线程被释放后,继续下一次循环 for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); // 获取头节点,从头结点开始释放,由于count已经降为0,所以r >= 0为true // 然后会将自己摘除当前队列,使下一个节点成为头节点 // 等下一个节点也恢复过来后,同样执行上面的过程 // 这样,队列中的所有线程就被释放了 if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } // 将node的waitStatus设置为-1(常量SIGNAL,表示需要唤醒),并阻塞 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } 6 总结 本文从源码层面详细说明了CountDownLatch是如何运作的。 CountDownLatch也是基于AQS实现,所以了解AQS的机制,对于理解本文至关重要。 其实,CountDownLatch最核心的就是通过控制AQS的state,来同步多个线程之间的状态。

优秀的个人博客,低调大师

QLExpress代码解读,运行原理解析

本文针对上图的功能详细图,进行逐个的简单介绍:代码入口、代码的主要逻辑和算法。 调用代码实例 //本文以helloworld案例,开启了两个打印日志的参数,实际使用通常不建议打开。 boolean printParseLog = true;//语法分析日志开关 boolean printExecuteLog = true;//每一条指令执行开关 ExpressRunner runner = new ExpressRunner(false, printParseLog); IExpressContext<String, Object> context = new DefaultContext<String, Object>(); String script = "sum=0;for(i=0;i<10;i=i

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册