协程, Coroutine

协程, Coroutine 协程别名: 微线程,纤程。英文:Coroutine, Green threads, fibers 传统编程语言中子程序或者函数是层级调用的,函数可以调用其它函数, 调用者需要等待被调用者结束之后继续执行, 函数调用是通过栈实现的. 一个线程就是按顺序执行一个或几个子函数, 函数调用只有一个入口和一个出口. 协程看上去也是函数,但是执行过程中在子程序内部可以中断,然后执行别的函数, 然后再被调度回来执行. 协程比线程有更高的执行效率, 协程没有线程切换的开销 协程在用户空间调度, 不涉及系统调用或任何阻塞调用, 不需要用来守卫关键区块的同步性原语(primitive)比如互斥锁、信号量等,并且不需要来自操作系统的支持 协程不需要多线程的锁机制, 为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 协程是协作式多任务的, 线程典型是抢占式多任务的 因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。 使用抢占式调度的线程实现协程,但是会失去某些利益(特别是对硬性实时操作的适合性和相对廉价的相互之间切换)。 协程是语言层级的构造,可看作一种形式的控制流,而线程是系统层级的构造 生成器 生成器,也叫作“半协程”[8],是协程的子集。 https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824 https://zh.wikipedia.org/wiki/%E5%8D%8F%E7%A8%8B 有栈协程 有栈协程的好处,由于栈帧可以直接完全保存运行期上下文(主要是寄存器值),因此可以在任何时刻暂停协程的运行,这就很方便地支持了抢占式的调度器。 无栈协程 有栈协程的好处,由于栈帧可以直接完全保存运行期上下文(主要是寄存器值),因此可以在任何时刻暂停协程的运行,这就很方便地支持了抢占式的调度器。而无栈协程的上下文是一般通过类似结构体的方式保存在内存中,它依赖使用者显式地切换协程,否则协程不会主动让出执行权。 另外,有栈协程更方便将同步代码改造为异步代码,就像我们的例子一样,只需改动一行,加上go关键字就可以了。而无栈协程,同步改造为异步则更为复杂,甚至会导致牵一发动全身(async关键字扩散问题)。 Rust无栈协程 既然已经有了有栈协程,那么无栈协程是否还有优势呢。答案肯定的! 通常,无栈协程在内存空间和协程上下文切换的效率更高。值得说明的是,无栈协程并不是说不需要运行时的栈空间,而是和协程的创建者共用同一块运行时的栈空间。 如果一定要用一句话概括无栈协程,那就是:无栈协程可以看做是有状态的函数(generator),每次执行时会根据当前的状态和输入参数,得到(generate)输出,但不一定为最终结果。

2015-02-04 · 1 min · 39 words · -

Conway's law, 康威定律

Conway’s law, 康威定律 康威 (梅尔·康威)定律 任何组织在设计一套系统 (广义概念上的系统)时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。 https://www.cnblogs.com/ghj1976/p/5703462.html 康威定律——“设计系统的组织,最终产生的设计等同于组织之内、之间的沟通结构。” 康威定律,其内涵是: 设计系统的组织,最终产生的设计等同于组织之内、之间的沟通结构。 Conway’s law(康威定律) Posted on 2016-07-25 14:43 蝈蝈俊 阅读(1952) 评论(0) 编辑 收藏 Mel Conway 康威在加利福尼亚理工学院获得物理学硕士学位,在凯斯西储大学获得数学博士学位。毕业之后,他参与了很多知名的软件项目,如 Pascal 编辑器。在他的职业生涯中,康威观察到一个现象: 软件团队开发的产品是对公司组织架构的反映。 1967 年他针对这个现象提交了一篇论文。 (http://www.melconway.com/Home/Conways_Law.html) 给 《哈佛商业评论》。结果程序员屌丝的文章不入商业人士的法眼,无情被拒,康威就投到了一个编程相关的杂志,所以被误解为是针对软件开发的。 最初这篇文章显然不敢自称定律 (law) ,只是描述了作者自己的发现和总结。后来,在Brooks Law著名的人月神话中,引用这个论点,并将其"吹捧"成了现在我们熟知"康威定律"。 image 康威定律的核心如下: Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure. 任何设计系统的组织,必然会产生以下设计结果: 即其结构就是该组织沟通结构的写照。简单来说: 产品必然是其组织沟通结构的缩影。 http://tech2ipo.com/102149

2015-01-29 · 1 min · 65 words · -

安卓线刷升级, flash factory image for android

安卓线刷升级, flash factory image for android download factory image from https://developers.google.com/android/nexus/images https://developer.android.com/preview/get https://developers.google.com/android/images https://developer.android.com/about/versions/12/download wget https://dl.google.com/dl/android/aosp/angler-opr6.170623.013-factory-a63b2f21.zip #edit flash-all.sh and remove the -w command for linux https://wiki.archlinux.org/index.php/android#Detect_the_device 开发者模式 … unlock 开发者模式>oem unlock adb reboot bootloader fastboot flashing unlock # 按方向键切换到unlock # 按电源键解锁 手机开启 开发者模式,打开usb调试,oem unlock 将手机连接到电脑 # 进入root sudo -s #restart adb: adb kill-server adb start-server adb devices #reboot to bootloader: adb reboot bootloader #不用sudo会一直waiting device, 另外一种waiting device的情况 是连接 电脑的typc线有问题,比如只有充电功能...我的pixelbook带的线就不能刷机用。 ./flash-all.sh if failed, flash image manually ...

2015-01-29 · 2 min · 292 words · -

安卓查看WIFI密码

安卓查看WIFI密码 打开Root Exploere,进入data/misc/wifi/wpa_supplicant.conf

2015-01-28 · 1 min · 3 words · -

JaveEE

JaveEE http://www.iteye.com/topic/153734 1.MVC的各个部分都有那些技术来实现?如何实现? 答: MVC是Model-View-Controller的简写。“Model” 代表的是应用的业务逻辑 (通过JavaBean,EJB组件实现) , “View” 是应用的表示面 (由JSP页面产生) ,“Controller” 是提供应用的处理过程控制 (一般是一个Servlet) ,通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。 2.J2EE是什么? 答: Je22是Sun公司提出的多层(multi-diered),分布式(distributed),基于组件(component-base)的企业级应用模型(enterpriese application model).在这样的一个应用系统中,可按照功能划分为不同的组件,这些组件又可在不同计算机上,并且处于相应的层次(tier)中。所属层次包括客户层(clietn tier)组件,web层和组件,Business层和组件,企业信息系统(EIS)层。 http://www.wiloon.com/?p=1057 3.J2EE是技术还是平台还是框架? 答: J2EE本身是一个标准,一个为企业分布式应用的开发提供的标准平台。 J2EE也是一个框架,包括JDBC、JNDI、RMI、JMS、EJB、JTA等技术。 4.STRUTS的应用(如STRUTS架构) 答: Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。 采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。 Struts有如下的主要功能: 一.包含一个controller servlet,能将用户的请求发送到相应的Action对象。 二.JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。 三.提供了一系列实用对象: XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。 5.WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。 答: Web ServiceWeb Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作。 JAXP(Java API for XML Parsing) 定义了在Java中使用DOM, SAX, XSLT的通用的接口。这样在你的程序中你只要使用这些通用的接口,当你需要改变具体的实现时候也不需要修改代码。 JAXM(Java API for XML Messaging) 是为SOAP通信提供访问方法和传输机制的API。 WSDL是一种 XML 格式,用于将网络服务描述为一组端点,这些端点对包含面向文档信息或面向过程信息的消息进行操作。这种格式首先对操作和消息进行抽象描述,然后将其绑定到具体的网络协议和消息格式上以定义端点。相关的具体端点即组合成为抽象端点 (服务) 。 SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML编码信息的轻量级协议。 ...

2015-01-22 · 2 min · 220 words · -

JSR303

JSR303 http://blog.csdn.net/caihaijiang/article/details/7463514 JSR 303 – Bean Validation 是一个数据验证的规范。 在任何时候,当你要处理一个应用程序的业务逻辑时,必须要考虑数据校验,确保输入进来的数据从语 义上来讲是正确的。在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成。很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余、不利于维护等问题。使用Bean Validation,将验证逻辑与相应的域模型进行绑定,能够很好的避免发生这样的问题。 Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint,例如@NotNull,@Max,@Size, 就可以确保数据模型 (JavaBean) 的正确性。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。 Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。借助Hibernate Validator,可以很好的进行参数验证。 例子如下 (摘自参考文档2的内容) : maven依赖: <dependency> <groupId>com.alibaba.external</groupId> <artifactId>sourceforge.hibernate.validator</artifactId> <version>4.0.2.GA</version> </dependency> <dependency> <groupId>com.alibaba.external</groupId> <artifactId>java.validation.api</artifactId> <version>1.0.0.GA</version> </dependency> <dependency> <groupId>com.alibaba.external</groupId> <artifactId>org.slf4j.slf4j-api</artifactId> <version>1.5.6</version> </dependency> <dependency> <groupId>com.alibaba.external</groupId> <artifactId>org.slf4j.slf4j-log4j12</artifactId> <version>1.5.6</version> </dependency> <dependency> <groupId>com.alibaba.external</groupId> <artifactId>jakarta.log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>com.alibaba.external</groupId> <artifactId>sourceforge.spring</artifactId> <version>2.5.6</version> </dependency> 2. 要校验的Java Bean ```java package com.mycompany; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) private String licensePlate; @Min(2) private int seatCount; public Car(String manufacturer, String licencePlate, int seatCount) { this.manufacturer = manufacturer; this.licensePlate = licencePlate; this.seatCount = seatCount; } //getters and setters ... } 3. 进行的校验 ```java import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.BeforeClass; import org.junit.Test; public class CarTest { private static Validator validator; @BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void manufacturerIsNull() { Car car = new Car(null, "DD-AB-123", 4); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(1, constraintViolations.size()); assertEquals("may not be null", constraintViolations.iterator().next().getMessage()); } @Test public void licensePlateTooShort() { Car car = new Car("Morris", "D", 4); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(1, constraintViolations.size()); assertEquals("size must be between 2 and 14", constraintViolations.iterator().next().getMessage()); } } 更详细的使用,请看参考文档2. 参考文档: 1. JSR 303: http://jcp.org/en/jsr/summary?id=303 2. HIBERNATE Validator: http://docs.jboss.org/hibernate/validator/4.2/reference/en-US/pdf/hibernate_validator_reference.pdf 3. 中文版的: http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html/validator-usingvalidator.html

2015-01-22 · 2 min · 236 words · -

SSH 端口转发, ssh port forward

SSH 端口转发, ssh port forward 端口转发, ssh port forward 比如在一台远程主机上运行着 Grafana: 192.168.50.100:32178, 但是 192.168.50.100 只对 192.168.50.50 开放了 22 端口, 我想从 192.168.50.50 访问 192.168.50.100 的 Grafana 就可以用 ssh 的端口转发 # -L: local port forwarding # 在 192.168.50.50 上执行 ssh -L 32179:192.168.50.100:32178 192.168.50.100 -l user0 # 32179 本地端口, 用户可以在 192.168.50.50 上用浏览器直接访问本地的 32179 端口的 grafana, http://127.0.0.1:32179 # 192.168.50.100:32178, 要访问的 ip 和 端口, 在 192.168.50.50 上访问 127.0.0.1:32179 就相当于访问 192.168.50.100:32178 # 192.168.50.100 -l user0, ssh 连接的主机和用户名, 这里用了默认的 ssh 22 端口 ssh -L 2000:192.168.50.11:5432 192.168.50.10 -l root # 2000 本地端口 # 192.168.50.11 目标主机 # 5432 目标端口 # 192.168.50.10 跳板机, 运行 ssh 服务端的主机, 并且 从192.168.50.10 能访问 192.168.50.11:5432 https://wangdoc.com/ssh/port-forwarding.html ...

2015-01-18 · 2 min · 386 words · -

git submodule

git submodule 有 submodule 的 git 仓库根目录会有 .gitmodules 文件。 # check submodule with command git submodule status # 有输出就是有 submodule 当你在一个 Git 项目上工作时,你需要在其中使用另外一个Git 项目。也许它是一个第三方开发的Git 库或者是你独立开发和并在多个父项目中使用的。这个情况下一个常见的问题产生了: 你想将两个项目单独处理但是又需要在其中一个中使用另外一个。 在 Git 中你可以用子模块 submodule 来管理这些项目,submodule 允许你将一个 Git 仓库当作另外一个 Git 仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。 主仓库切换分支之后,子仓库并不会跟着一起切换, 得在主仓库上执行一次 git submodule update git submodule update –init 将 git submodule init 和 git submodule update 合并成一步。如果还要初始化、抓取并检出任何嵌套的子模块, 请使用简明的 git submodule update –init –recursive。 # 添加 submodule # 为已有的 git 仓库增加子模块, 命令执行完成,会在当前工程根路径下生成一个名为“.gitmodules”的文件 # enx: 子模块的目录名 git submodule add git@github.com:wiloon/enx.git enx # 已经配置子模块的仓库, 主项目和子模块一起克隆 git clone -b branch_0 git@github.com:foo/bar.git --recursive # 查看子模块, 如果 git submodule 返回的 hash 前面有一个减号, 代表子模块还没有检出, 加号代表 submodule 距离上一次跟主仓库关联的 commit id 有新的 commit, 这时在主仓库里对 submodule 所在的目录做 git add folder0 之后 git submodule 命令返回的数据不再有加号. # git submodule 返回的 commit id 是当前 submodule 目录当前的 commit id # commit id 前面 的加号代表远程仓库关联的submodule 有更新, 执行 git submodule update 之后 , submodule 的版本会更新到与远程主仓库关联的submodule commit id 一致. git submodule # 比如只克隆了主仓库, submodule 所在的目录肯定是空的, 要用这个命令初始化一下 submodule, 然后再执行 git submodule update, submodule 目录就克隆下来了. git submodule init # 把submodule 更新到跟远程主仓库关联的 commit id 一致, git status 应该是 clear 的 git submodule update # 更新指定的 submodule 到远程仓库的最新版本 git submodule update --init --remote <submodule_path> # 把 submodule 更新到子仓库最新的 commit id, 这个 commit 有可能跟之前关联的 commit id 不一样, 一般会比之前 关联的 commit id 更新, git status 会看到 submodule 有变更需要提交, 需要更新 关联的 commit id. git submodule update --remote # 拉取子模块 的代码 git submodule update --init --recursive 使用 submodule, 主仓库 git pull 之后, submodule 不会自动更新, 还要检查一下 submodule 的版本, 可能需要执行git submodule update 更新 一下. ...

2015-01-18 · 2 min · 253 words · -

Go bufio

golang bufio bufio 对 io 进行了包装, 提供了缓冲. bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。 简单的说就是bufio会把文件内容读取到缓存中 (内存),然后再取读取需要的内容的时候,直接在缓存中读取,避免文件的i/o操作。同样,通过bufio写入内容,也是先写入到缓存中 (内存),然后由缓存写入到文件。避免多次小内容的写入操作I/O。 bufio.Read(p []byte) 的思路如下: 1、当缓存区有内容的时,将缓存区内容全部填入p并清空缓存区 2、当缓存区没有内容的时候且len(p)>len(buf),即要读取的内容比缓存区还要大,直接去文件读取即可 3、当缓存区没有内容的时候且len(p)<len(buf),即要读取的内容比缓存区小,缓存区从文件读取内容充满缓存区,并将p填满 (此时缓存区有剩余内容) 4、以后再次读取时缓存区有内容,将缓存区内容全部填入p并清空缓存区 (此时和情况1一样) https://www.cnblogs.com/ricklz/p/13188188.html http://www.cnblogs.com/golove/p/3282667.html // bufio 包实现了带缓存的 I/O 操作 type Reader struct { … } // NewReaderSize 将 rd 封装成一个带缓存的 bufio.Reader 对象, // 缓存大小由 size 指定 (如果小于 16 则会被设置为 16) 。 // 如果 rd 的基类型就是有足够缓存的 bufio.Reader 类型,则直接将 // rd 转换为基类型返回。 func NewReaderSize(rd io.Reader, size int) *Reader // NewReader 相当于 NewReaderSize(rd, 4096) func NewReader(rd io.Reader) *Reader ...

2015-01-16 · 7 min · 1357 words · -

httpcomponent, httpclient proxy setting

httpcomponent, httpclient proxy setting http://www.jianshu.com/p/f38a62efaa96 HttpHost proxy = new HttpHost("localhost",8888); CloseableHttpClient httpclient = HttpClients.custom() .setDefaultRequestConfig(RequestConfig.custom() .setProxy(proxy).build()).build();

2015-01-16 · 1 min · 16 words · -

zookeeper

zookeeper [16/11/21 03:20:30:030 CST] main-SendThread(192.168.50.100:2181) WARN zookeeper.ClientCnxn: Session 0x0 for server 192.168.50.100/:2181, unexpected error, closing socket connection and attempting reconnect 检查zookeeper包版本和连接的服务端版本,有可能是版本不一致 https://blog.csdn.net/richie696/article/details/112910751 向 zookeeper 发送 stat 命令 查询 zookeeper版本 echo stat | socat - TCP:192.168.50.100:2181 server # docker docker run \ --name zookeeper \ -p 2181:2181 \ -v /etc/localtime:/etc/localtime:ro \ -v zookeeper-conf:/conf \ -v zookeeper-data:/data \ -v zookeeper-datalog:/datalog \ -d \ zookeeper # podman podman run \ --name zookeeper \ -p 2181:2181 \ -v /etc/localtime:/etc/localtime:ro \ -e ZOO_4LW_COMMANDS_WHITELIST=* \ -d \ zookeeper # conf, data volume podman run \ --name zookeeper \ -p 2181:2181 \ -v /etc/localtime:/etc/localtime:ro \ -v zookeeper-conf:/conf \ -v zookeeper-data:/data \ -v zookeeper-datalog:/datalog \ -e ZOO_4LW_COMMANDS_WHITELIST=* \ -d \ zookeeper:3.7.0 # client docker run -it --rm zookeeper zkCli.sh -server 127.0.0.1 #zkCli.sh #连接zookeeper bin/zkCli.sh -server localhost:2181 #创建节点 create /k0 v0 # 删除一个节点 delete /k0 install download zookeeper cp zoo_sample.cfg zoo.cfg ...

2015-01-14 · 2 min · 425 words · -

thread join

thread join thread join 之后,主线程的状态是waiting 一、在研究join的用法之前,先明确两件事情。 1.join方法定义在Thread类中,则调用者必须是一个线程, 例如: Thread t = new CustomThread();//这里一般是自定义的线程类 t.start();//线程起动 t.join();//此处会抛出InterruptedException异常 2.上面的两行代码也是在一个线程里面执行的。 以上出现了两个线程,一个是我们自定义的线程类,我们实现了run方法,做一些我们需要的工作;另外一个线程,生成我们自定义线程类的对象,然后执行 customThread.start(); customThread.join(); 在这种情况下,两个线程的关系是一个线程由另外一个线程生成并起动,所以我们暂且认为第一个线程叫做"子线程",另外一个线程叫做"主线程"。 二、为什么要用join()方法 主线程生成并起动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join();方法了。 三、join方法的作用 在网上看到有人说"将两个线程合并"。这样解释我觉得理解起来还更麻烦。不如就借鉴下API里的说法: “等待该线程终止。” 解释一下,是主线程(我在"一"里已经命名过了)等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。(Waits for this thread to die.) 四、用实例来理解 写一个简单的例子来看一下join()的用法,一共三个类: 1.CustomThread 类 CustomThread1类 JoinTestDemo 类,main方法所在的类。 代码1: package wxhx.csdn2; /** * * @author bzwm * */ class CustomThread1 extends Thread { public CustomThread1() { super("[CustomThread1] Thread"); }; public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); try { for (int i = 0; i < 5; i++) { System.out.println(threadName + " loop at " + i); Thread.sleep(1000); } System.out.println(threadName + " end."); } catch (Exception e) { System.out.println("Exception from " + threadName + ".run"); } } } class CustomThread extends Thread { CustomThread1 t1; public CustomThread(CustomThread1 t1) { super("[CustomThread] Thread"); this.t1 = t1; } public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); try { t1.join(); System.out.println(threadName + " end."); } catch (Exception e) { System.out.println("Exception from " + threadName + ".run"); } } } public class JoinTestDemo { public static void main(String[] args) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); CustomThread1 t1 = new CustomThread1(); CustomThread t = new CustomThread(t1); try { t1.start(); Thread.sleep(2000); t.start(); t.join();//在代碼2里,將此處注釋掉 } catch (Exception e) { System.out.println("Exception from main"); } System.out.println(threadName + " end!"); } } 打印结果: main start.//main方法所在的线程起动,但没有马上结束,因为调用t.join();,所以要等到t结束了,此线程才能向下执行。 [CustomThread1] Thread start.//线程CustomThread1起动 [CustomThread1] Thread loop at 0//线程CustomThread1执行 [CustomThread1] Thread loop at 1//线程CustomThread1执行 [CustomThread] Thread start.//线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。 [CustomThread1] Thread loop at 2//线程CustomThread1继续执行 [CustomThread1] Thread loop at 3//线程CustomThread1继续执行 [CustomThread1] Thread loop at 4//线程CustomThread1继续执行 [CustomThread1] Thread end. //线程CustomThread1结束了 [CustomThread] Thread end.// 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果 main end!//线程CustomThread结束,此线程在t.join();阻塞处起动,向下继续执行的结果。 修改一下代码,得到代码2: (这里只写出修改的部分) ```java public class JoinTestDemo { public static void main(String[] args) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " start."); CustomThread1 t1 = new CustomThread1(); CustomThread t = new CustomThread(t1); try { t1.start(); Thread.sleep(2000); t.start(); // t.join();//在代碼2里,將此處注釋掉 } catch (Exception e) { System.out.println("Exception from main"); } System.out.println(threadName + " end!"); } } 打印结果: main start. // main方法所在的线程起动,但没有马上结束,这里并不是因为join方法,而是因为Thread.sleep(2000); [CustomThread1] Thread start. //线程CustomThread1起动 [CustomThread1] Thread loop at 0//线程CustomThread1执行 [CustomThread1] Thread loop at 1//线程CustomThread1执行 main end!// Thread.sleep(2000);结束,虽然在线程CustomThread执行了t1.join();,但这并不会影响到其他线程(这里main方法所在的线程)。 [CustomThread] Thread start. //线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。 [CustomThread1] Thread loop at 2//线程CustomThread1继续执行 [CustomThread1] Thread loop at 3//线程CustomThread1继续执行 [CustomThread1] Thread loop at 4//线程CustomThread1继续执行 [CustomThread1] Thread end. //线程CustomThread1结束了 [CustomThread] Thread end. // 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果 五、从源码看join()方法 在CustomThread的run方法里,执行了t1.join();,进入看一下它的JDK源码: ```java public final void join() throws InterruptedException { n(0); } 然后进入join(0)方法: ```java /** * Waits at most `millis` milliseconds for this thread to * die. A timeout of `` means to wait forever. //注意这句 * * @param millis the time to wait in milliseconds. * @exception InterruptedException if another thread has interrupted * the current thread. The _interrupted status_ of the * current thread is cleared when this exception is thrown. */ public final synchronized void join(long millis) //参数millis为0. throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) {//进入这个分支 while (isAlive()) {//判断本线程是否为活动的。这里的本线程就是t1. wait(0);//阻塞 } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } 单纯从代码上看,如果线程被生成了,但还未被起动,调用它的join()方法是没有作用的。将直接继续向下执行,这里就不写代码验证了。 http://blog.csdn.net/bzwm/article/details/3881392

2015-01-13 · 3 min · 503 words · -

苹果 WebApp

苹果 WebApp 苹果真的要在 AppStore 里封杀 WebApp 吗? http://fins.iteye.com/blog/1685886 苹果真的要在 AppStore 里封杀 WebApp 吗 ? 最近几个月, 苹果AppStore似乎加强了对WebApp的管控, 很多过去能上架的 使用WebApp+Native壳的应用陆陆续续的都被拒了. 于是 很多人开始抛出了"苹果要封杀WebApp"/"苹果要像当初对待Flash一样对HTML5说不"一类的观点. 作为一个HTML5开发人员 + 苹果产品用户, 我也想表达一下自己对这个问题的看法. 我的观点不一定对 但是,即使我错了,也不能证明那些认为"苹果要封杀WebApp"的荒谬观点是正确的(好流氓 哈哈). 先来看一看让广大HTML5/WebApp开发者 感动忧虑的那段苹果的原文吧: 引用 If you cannot – or choose not to – revise your app to be in compliance with the App Store Review Guidelines, you may wish to build an HTML5 web app instead. You can distribute web apps directly on your web site; the App Store does not accept or distribute web apps. 简单说就是一句话: 如果你的应用是一个Webapp, 那么请以网页的形式发布你的产品就好了, 不要放到AppStore里, AppStore不接收WebApp. 不管怎么看 我都看不出来"苹果要封杀WebApp"的意思, 更看不出有些人YY的"苹果因为担心HTML5太强大了抢了Native的市场"这种观点. 相反 我觉得苹果是在引导WebApp用正确的方式去发行: 如果你的应用在网页里也能跑, 但你却非要放到AppStore里, 结果就是赚了钱还要分给苹果30%, 而且更新升级什么的还要走漫长的审核过程,何苦呢? 在AppStore方面, 苹果是靠应用(注意,是应用,而不是和某种具体技术绑定的应用.只要是合法的 好的应用,受欢迎卖得多,苹果都能赚钱,苹果才不关心应用用的是什么技术呢)分成赚钱, 如果纯粹从经济目的出发, 苹果完全没必要把WebApp从他能赚钱的领域(AppStore应用)驱赶到他不能赚钱的领域(Web浏览器). 所以 一个合法的应用被拒绝的原因笼统的说只有三点: 1 违规(调用不该调用的方法,做了危险的事情,山寨抄袭等等) 2 苹果觉得应用不够好 3 觉得放到AppStore里不合适. 前两点不用说大家都懂, 而最后一点我想是大量WebApp被拒绝的一个主要原因: 完全没有使用或者没必要使用任何Native的技术,在网页里也能跑. 通常这种应用只是把AppStore当做一个发行渠道. 我特意去AppStore上搜索了下, 其实存在大量的Phonegap封装的应用, 我挑了几个免费的下来,解包看了一下, 它们都使用到了Phonegap提供的一些只有native技术才能实现的功能, 我想这是他们能通过审核的一个很重要的原因之一. ========================= 还有朋友提出了这样一个观点:“app store的意义是维护苹果利益,webapp可以同时存在多个平台,就会降低apple独占的市场份额,直接影响利益。” ...

2015-01-12 · 1 min · 173 words · -

Pareto principle, 帕累托法则, 二八定律

Pareto principle, 帕累托法则, 二八定律 二八定律又名80/20定律、帕累托法则 (定律) 也叫巴莱特定律、最省力的法则、不平衡原则等,被广泛应用于社会学及企业管理学等。[1] 1897年,意大利经济学者帕累托偶然注意到19世纪英国人的财富和收益模式。 二八定律 在调查取样中,发现大部份的财富流向了少数人手里。同时,他还从早期的资料中发现,在其他的国家,都发现有这种微妙关系一再出现,而且在数学上呈现出一种稳定的关系。于是,帕累托从大量具体的事实中发现: 社会上20%的人占有80%的社会财富,即: 财富在人口中的分配是不平衡的。 同时,人们还发现生活中存在许多不平衡的现象。因此,二八定律成了这种不平等关系的简称,不管结果是不是恰好为80%和20% (从统计学上来说,精确的80%和20%出现的概率很小) 。习惯上,二八定律讨论的是顶端的20%。而非底部的80%。人们所采用的二八定律,是一种量化的实证法,用以计量投入和产出之间可能存在的关系。[2]“长尾理论"被认为是对传统的"二八定律"的彻底叛逆。[2] 如右图所示,横轴是品种,纵轴是销量。典型的情况是只有少数产品销量较高,其余多数产品销量很低。 传统的二八定律 (或称20/80定律) 关注其中红色部分,认为20%的品种带来了80%的销量,所以应该只保留这部分,其余的都应舍弃。长尾理论则关注蓝色的长尾巴,认为这部分积少成多,可以积累成足够大、甚至超过红色部分的市场份额。但也有很多失败者并没有真正理解长尾理论的实现条件。[2] http://baike.baidu.com/view/40591.htm?fromtitle=%E5%B8%95%E7%B4%AF%E6%89%98%E6%B3%95%E5%88%99&fromid=7224763&type=syn

2015-01-08 · 1 min · 20 words · -

做与作

“作”与“做”的区别 “作”和“做”,都是常用字,音同,有时通用,所以使用时让人犯难,不知该用哪个字。 作,是古老的字,甲骨文里就有,最初的含义是“起”,现代汉语里仍然使用的“振作”、“一鼓作气”、“枪声大作”中的“作”,都是“起”的意思。在这个意义上跟“做”不会打架,因为“做”无此含义。 “作”和“做”,都有“从事”、“制作”、“充当”的含义,所以容易混淆。 做,是后造字,最早出现在宋、元时代,当“即使”、“播弄”、“做作”讲。到明代,“做”成了“作”的俗字,渐渐演变成为“作”的同义词。 那么,在使用时怎样区别“作”和“做”呢?20 世纪 80 年代,著名语言文字学家吕叔湘先生认为:“区别的办法基本上还是用‘文’和‘白’做标准,但不是绝对的。那么怎么办呢?我说,遇到没有把握的词,宁可写‘作’不写‘做’。”到了 20 世纪 90 年代,吕先生在《现代汉语八百词》中,对“作”、“做”二字的用法做了如下补充:“习惯上,具体东西的制造一般写成‘做’,如‘做桌子、做衣服、做文章’,抽象一点的、书面语言色彩重一点的词语,特别是成语里,一般都写成‘作’,如‘作罢、作废、作对、作怪、作乱、作价、作曲、作战、装模作样、认贼作父’。” 从吕叔湘先生的论述中,可以找到区分“作”、“做”用法的一般规律: (1)区别的基本标准是“文”和“白”,内容抽象或书面语言色彩重的词语,一般用“作”,成语都用“作”。 (2)做单音节动词用,多数用“做”,少数用“作”的,其宾语内容也比较抽象。宾语是动名词的(如:报告,调查,榜样,处理,动员,贡献),一般也用“作”。 (3)表身份、成绩、行为等义的“作为”,用“作”。 (4)遇到没有把握的词,宁可写“作”不写“做”,但要做到局部(一篇文章或一本书)统一。 基本词义: 作[zuō zuó zuò] 起,兴起,现在起:振~、枪声大~; 从事,做工:工~、~息、~业、合~; 3.举行,进行:~别(分别)、~乱、~案、~战、~报告; 干出,做出,表现出,制造出:~恶(è)、~弊、~梗、~祟、~态、~色、~为、~难、~奸犯科(为非作歹,触犯法令); 当成,充当:~罢、~保、~伐(做媒人)、~壁上观(人家交战,自己站在营垒上看,喻坐观别人成败)、~法自毙、~茧自缚; 写作、创造:创~、写~、~曲、~者; 文艺方面的成品:~品、不朽之~; 同“做”; 9.旧时手工业制造加工的场所:~坊; 10.从事某种活动:~揖、~弄、~死 做 [zuò]字义 1.进行工作或活动:~活、~事、~工、~手脚(暗中进行安排); 写文:~诗、~文章; 加工、制造:~饭、~模型、~衣服; 当,为:~人、~媒、~伴、~主、~客、看~; 5.装,扮:~作、~功、~派; https://zhuanlan.zhihu.com/p/70084978

2015-01-05 · 1 min · 40 words · -

将HTML5封装成android应用APK文件的几种方法

将HTML5封装成android应用APK文件的几种方法 http://mobile.51cto.com/android-386448.htm 越来越多的开发者热衷于使用html5+JavaScript开发移动Web App。不过,HTML5 Web APP的出现能否在未来取代移动应用,就目前来说,还是个未知数。一方面,用户在使用习惯上,不喜欢在浏览器上输入复杂的网址;另一方面,Html5 Web App 存放在服务器端,在每次使用时需要进行数据传递,会造成流量浪费。有些开发者不想接触复杂的JAVA代码,那么,有什么办法,既可以使用HTMl5开发应 用,又可以将其简单封装成APK文件呢? 一、Android SDK中的WebView 1.在要Activity中实例化WebView组件: WebView webView = new WebView(this); 2.调用WebView的loadUrl()方法,设置WevView要显示的网页: 互联网用: webView.loadUrl(“http://www.31358.com”); 本地文件用: webView.loadUrl(“file:///android_asset/XX.html”); 本地文件存放在: assets 文件中 3.调用Activity的setContentView( )方法来显示网页视图 4.用WebView点链接看了很多页以后为了让WebView支持回退功能,需要覆盖覆盖Activity类的onKeyDown()方法,如果不做任何处理,点击系统回退剪键,整个浏览器会调用finish()而结束自身,而不是回退到上一页面 5.需要在AndroidManifest.xml文件中添加权限,否则会出现Web page not available错误。 缺点: 如果是载入的是普通网页,没有什么问题,但如果是html5,封装后,在android2.3以上才能正常访问,android2.2及以下,SDK中的WebView还没完全支持HTML5 下面是具体例子: MainActivity.java package com.android.webview.activity; import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.webkit.WebView; public class MainActivity extends Activity { private WebView webview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //实例化WebView对象 webview = new WebView(this); ...

2014-12-30 · 1 min · 134 words · -

smali

smali http://blog.csdn.net/caszhao/article/details/6030425 je或jz //相等则跳 (机器码是74或84) jne或jnz //不相等则跳 (机器码是75或85) 常见的修改就是把对比部分的机器码中74改成75或者84改成85,在反编译的smali文件中,也是类似的。 相等比较符号在smali中的表示 符号 smali语法 Bao力破解修改 == if-eq if-eq改成if-ne != if-ne if-ne 改成 if-eq equals if-eqz if-eqz改成 if-nez !equals if-nez if-nez 改成if-eqz

2014-12-30 · 1 min · 29 words · -

QNAP

QNAP qnap, plex server, apple tv, av1 https://www.reddit.com/r/PleX/comments/12pe5tx/comment/jgrrjnc/ https://github.com/currifi/plex_av1_tvos?tab=readme-ov-file https://support.plex.tv/articles/202915258-where-is-the-plex-media-server-data-directory-located/ https://www.qnap.com/en/how-to/faq/article/how-do-i-access-my-qnap-nas-using-ssh QNAP 启用 ssh 服务 ssh admin@192.168.50.227 # print The exact data directory location getcfg -f /etc/config/qpkg.conf PlexMediaServer Install_path mkdir /share/CACHEDEV1_DATA/.qpkg/PlexMediaServer/Library/Plex Media Server/Profiles # copy file https://github.com/scriptsingh/plex_av1_tvos/blob/main/tvOS.xml to Profiles dir 重启 plex server 威联通 nas web ui> app center> plex server stop start

2014-12-30 · 1 min · 53 words · -

Android APK反编译

Android APK反编译 http://blog.csdn.net/ithomer/article/details/6727581 这段时间在学Android应用开发,在想既然是用Java开发的应该很好反编译从而得到源代码吧,google了一下,确实很简单,以下是我的实践过程。 在此郑重声明,贴出来的目的不是为了去破解人家的软件,完全是一种学习的态度,不过好像通过这种方式也可以去汉化一些外国软件。 本文Android反编译教程,测试环境: Win7 Ultimate x64 Ubuntu 12.04 x86_x64 反编译工具包 下载 (2012-10-10更新) 一、Apk反编译得到Java源代码 下载上述反编译工具包,打开apk2java目录下的dex2jar-0.0.9.9文件夹,内含apk反编译成java源码工具,以及源码查看工具。 apk反编译工具dex2jar,是将apk中的classes.dex转化成jar文件 源码查看工具jdgui,是一个反编译工具,可以直接查看反编译后的jar包源代码 dex2jar 和 jdgui 最新版本下载,分别见google code: dex2jar (google code) jdgui (google code) ,最新版本请见 官方 具体步骤: 首先将apk文件后缀改为zip并解压,得到其中的classes.dex,它就是java文件编译再通过dx工具打包而成的,将classes.dex复制到dex2jar.bat所在目录dex2jar-0.0.9.9文件夹。 在命令行下定位到dex2jar.bat所在目录,运行 dex2jar.bat classes.dex 生成 classes_dex2jar.jar 然后,进入jdgui文件夹双击jd-gui.exe,打开上面生成的jar包classes_dex2jar.jar,即可看到源代码了,如下图: HelloAndroid源码在反编译前后的对照如下: 二、apk反编译生成程序的源代码和图片、XML配置、语言资源等文件 如果是汉化软件,这将特别有用 首先还是要下载上述反编译工具包,其中最新的apktool,请到google code下载 apktool (google code) 具体步骤: 下载上述反编译工具包,打开apk2java目录下的apktool1.4.1文件夹,内含三个文件: aapt.exe,apktool.bat,apktool.jar 注: 里面的apktool_bk.jar是备份的老版本,最好用最新的apktool.jar 在命令行下定位到apktool.bat文件夹,输入以下命令: apktool.bat d -f abc123.apk abc123,如下图: 上图中,apktool.bat 命令行解释: apktool.bat d -f [apk文件 ] [输出文件夹] 反编译的文件如下 (AndroidManifest.xml为例) : 将反编译完的文件重新打包成apk,很简单,输入apktool.bat b abc123 (你编译出来文件夹) 即可,命令如下: ...

2014-12-30 · 1 min · 93 words · -

BDEV, CDEV

字符设备 提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。举例来说,键盘、串口、调制解调器都是典型的字符设备。 块设备, block device 块设备也就是存储以“块”为单位数据的设备,比较典型的如磁盘设备, 硬盘、软盘,光盘或者优盘/闪存。在 Linux 系统中,一切皆文件,磁盘设备也是文件 应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。数据的读写只能以块(通常是512B)的倍数进行 与字符设备不同,块设备并不支持基于字符的寻址。 用 ls 查看磁盘分区文件 ls -l /dev/sd* # output ➜ ~ ls -l /dev/sd* brw-rw---- 1 root disk 8, 0 Apr 1 07:15 /dev/sda brw-rw---- 1 root disk 8, 1 Apr 1 07:15 /dev/sda1 brw-rw---- 1 root disk 8, 2 Apr 1 07:15 /dev/sda2 brw-rw---- 1 root disk 8, 3 Apr 1 07:15 /dev/sda3 字符设备与块设备的区别 这两种类型的设备的根本区别在于它们是否可以被随机访问——换句话说就是,能否在访问设备时随意地从一个位置跳转到另一个位置。举个例子,键盘这种设备提供的就是一个数据流,当你敲入"fox" 这个字符串时,键盘驱动程序会按照和输入完全相同的顺序返回这个由三个字符组成的数据流。如果让键盘驱动程序打乱顺序来读字符串,或读取其他字符,都是没有意义的。所以键盘就是一种典型的字符设备,它提供的就是用户从键盘输入的字符流。对键盘进行读操作会得到一个字符流,首先是"f",然后是"o",最后是"x",最终是文件的结束(EOF)。当没人敲键盘时,字符流就是空的。硬盘设备的情况就不大一样了。硬盘设备的驱动可能要求读取磁盘上任意块的内容,然后又转去读取别的块的内容,而被读取的块在磁盘上位置不一定要连续,所以说硬盘可以被随机访问,而不是以流的方式被访问,显然它是一个块设备。 内核管理块设备要比管理字符设备细致得多,需要考虑的问题和完成的工作相比字符设备来说要复杂许多。这是因为字符设备仅仅需要控制一个位置—当前位置—而块设备访问的位置必须能够在介质的不同区间前后移动。所以事实上内核不必提供一个专门的子系统来管理字符设备,但是对块设备的管理却必须要有一个专门的提供服务的子系统。不仅仅是因为块设备的复杂性远远高于字符设备,更重要的原因是块设备对执行性能的要求很高;对硬盘每多一分利用都会对整个系统的性能带来提升,其效果要远远比键盘吞吐速度成倍的提高大得多。另外,我们将会看到,块设备的复杂性会为这种优化留下很大的施展空间。 字符设备与块设备的区分 每一个字符设备或者块设备都在/dev目录下对应一个设备文件。读者可以通过查看/dev目录下的文件的属性,来区分设备是字符设备还是块设备。使用cd命令进入/dev目录,并执行ls -l命令就可以看到设备的属性 [root@tom /]# cd /dev /进入/dev目录/ [root@tom dev]# ls -l /列出/dev中文件的信息/、 /*第1字段 2 3 4 5 6 7 8 */ crw-rw—-+ 1 root root 14, 12 12-21 22:56 adsp crw——- 1 root root 10, 175 12-21 22:56 agpgart crw-rw—-+ 1 root root 14, 4 12-21 22:56 audio brw-r—– 1 root disk 253, 0 12-21 22:56 dm-0 brw-r—– 1 root disk 253, 1 12-21 22:56 dm-1 crw-rw—- 1 root root 14, 9 12-21 22:56 dmmidi ...

2014-12-24 · 2 min · 225 words · -