首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

1、python机器学习基础教程——简述

一、引言: 机器学习(machine learning):从数据中提取知识。分为:监督学习(supervised learning)和无监督学习(unsupervised learning)。 二、监督机器学习应用: 1、识别信封上面的手写的邮政编码。 2、基于医学影像判断肿瘤是否为良性。 3、检测信用卡交易中的诈骗行为。 三、无监督机器学习应用: 1、确定一系列博客文章的主题。 2、将客户分成具有相似偏好的群组。 3、检测网站的异常访问模式。 四、构建机器学习解决方案过程中的思考: 1、要回答的问题是什么?已收集到的数据能够回答这个问题吗? 2、要将我的问题表示成机器学习问题,用哪种方法最好? 3、我收集的数据是否足够表达我想要解决的问题? 4、我提取了数据的哪些特征?这些特征能否实现正确的预测? 5、如何衡量应用是否成功? 6、机器学习解决方案与我的研究或商业产品中的其他部分是如何相互影响的? 五、编程环境Anaconda3.4.2+(python3.5+) anaconda安装遇到的问题请查看 conda httperror http none none for url none Anaconda更新失败解决办法 https://www.jianshu.com/p/c74668743932 pandas==0.18.1 matplotlib==1.5.1 numpy==1.11.1 scipy==0.17.1 scikit-learn==0.18

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

c++基础(上) 听课流水账

1、pass by value / pass by pointer / pass by reference pass by value:实参和形参不是同一个值,因此交换的是形参的值,当函数swap结束后,a和b的值并没有发生交换 pass by pointer and pass by reference :实参和形参是相同的。 2、动态内存 申请失败的返回值是 0 而不是 NULL, null在cpp中废止了。 申请一个长度我10,初始值都为0的空间 int *num= new int[10](); 3、对比new和malloc 4、cpp内存模型 内存模型:栈、堆、静态区、常量区。 5、数组内存模型 5、const char * / char * const / char const * 在c中,const是只读变量,cpp里const指的是常量。 6、常量指针和指针常量 下图为p1常量指针——p1所指地址上的内容不可以通过p1做出改动 下图p2为指针常量——p2所指的地址不可以发生改动 数组名就是一个指针常量 实例: 下图中的int const* pi和const int* pci是一样的 7、UML类图 一些编码规范:类名首字母大写; 8、匿名对象 9、访问对象成员 返回值类型 类名: : 函数名 10、cpp的string类型 11、访问器与更改器 12、同名屏蔽 #include "pch.h" #include<iostream> using namespace std; class Foo { public: int x; int y; Foo() { x = 10; y = 10; } void p() { int x = 20; cout << x << endl; cout << y << endl; } }; int main(int argc, char **argv) { Foo foo; foo.p(); return 0; } 运行结果: 20 10 13、成员函数的this指针 14、避免同名屏蔽的简单方法 15、对象数组 #include "pch.h" #include <iostream> using namespace std; class C { public: C(int i) { //带参构造函数 std::cout << "\t parameterized ctor" << std::endl; } C() { //无参(默认)构造函数 std::cout << "\t default ctor" << std::endl; } C(const C& c) { //拷贝构造函数 std::cout << "\t copy ctor" << std::endl; } C& operator =(const C& c) { //重载了类C的赋值运算符,观察main()中的对象赋值现象 std::cout << "\t assignment operator" << std::endl; return *this; } }; int main() { cout << "C c1;" << endl; C c1; //调用无参(默认)构造函数 cout << "C c2(2);" << endl; C c2(2); //调用有参构造函数 cout << "C c3 = c1;" << endl; C c3 = c1; //调用拷贝构造函数 cout << "C c4 = C();" << endl; C c4 = C(); //编译器将之等价为 C c4; cout << "C c5 = C(5);" << endl; C c5 = C(5); //编译器将之等价为 C c5(5); cout << "C c6(c1);" << endl; C c6(c1); //调用拷贝构造函数 cout << "C c7(C(7));" << endl; C c7(C(7)); //编译器将之等价为 C c7(7); cout << "C c8[4] = {C(), C(18), c7};" << endl; C c8[4] = { C(), C(18), c7 }; //c8中的4个元素初始化时,是看做4个独立的对象处理: //C c80 = C(); 调默认构造函数 //C c81 = C(18); 调有参构造函数 //C c82 = c7; 调拷贝构造函数 //C c83 = C(); 调默认构造函数 cout << "C c9; c9 = C(9);" << endl; C c9; //调用默认构造函数 c9 = C(9); //先调用有参构造函数构造一个匿名对象C(9),然后调用赋值运算符将该匿名对象赋值给c9 return 0; } 16、封装 当你的数据域是对象类型的时候,他必须放在构造函数的初始化列表里面进行构建,除非这个数据域的对象类型中有默认的无参构造函数。 17、不可变对象 定义:创建之后,不可变对象不可修改,除非通过成员拷贝。 如何让类“不可改变”: 1、数据域均设置为私有属性。 2、没有接口,使得外部可以调用来更改你的数据。 3、也没有能够返回可变数据域对象的引用或指针的访问器。 不可变类的一个特征是线程安全的,所以他有用。 18、避免多次声明 19、static 本文件内的static函数只允许本文件访问 下图中的静态数据成员numberofObjects,被类Circle的对象们共享。 静态成员变量只能放在所有函数的外面进行初始化 numberOfObjects一定要初始化,不然会报错 20、使用静态成员函数的规则 静态成员函数的访问规则 21、析构函数 22、拷贝构造 拷贝构造函数的形式以及何时会调用拷贝构造函数 注意下图右下角的赋值 x c= b; ,构造出的c里面传入的是默认参数 1 。 浅拷贝和深拷贝(类中数据成员如果含有指针,一定要自己写拷贝构造函数,因为编译器默认给生成的是浅拷贝版的拷贝构造函数,容易出问题的) 拷贝构造函数是一种特殊的构造函数 23、写一个类,实现栈的功能 24、写一个类,实现vector 24、一些编码规范 25、继承 派生类不能继承的是: 1、构造函数(cpp 11 中已经允许) 2、析构函数 3、友元函数 4、拷贝赋值函数 派生类构造函数调用基类构造函数的方法 : 若没有显示的调用基类的构造函数,则编译器默认调用无参的基类构造函数 (如果你写的构造函数有参,则编译器会报错,因为找不到无参的积累构造函数) 构造和析构的顺序 26、重定义 派生类对父类的同名函数内容进行修改。不修改的地方: 1、返回值类型 2、传入的参数 3、函数名 区分重定义和重载 27、绑定 基类中定义的虚函数,在派生类中的同名函数会自动变成虚函数,无需添加virtual关键字。 总结静态绑定和动态绑定 下面四张图的总结: 访问方式 访问对象 对象调用(p.f()) 子类同名函数 父类指针指向子类对象(图2) 父类同名函数 父类指针指向子类对象(图3) 子类的同名虚函数 28、protected protected成员可以被类内成员函数直接获取,不可以通过对象来获取。 29、几种继承方式的总结 30、抽象类与纯虚函数 抽象类(有纯虚函数的类是抽象类)不能被实例化 31、动态转换 32、向上转型和向下转型 向上转型可不使用dynamic_cast,而是隐式转换 向下转型必须使用dynamic_cast进行显示转换。 33、typeid运算符——用于获取对象所属的类的信息 可将派生类对象截取,只是用继承来的信息;但是不能将基类对象加长,无中生有变出派生类对象。(如下图:circle是派生类)

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

云计算基础知识:CPU虚拟化

【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 虚拟化技术的分类主要有服务器虚拟化、存储虚拟化、网络虚拟化、应用虚拟化。 服务器虚拟化技术按照虚拟对象来分,可分为:CPU虚拟化、内存虚拟化、I/O虚拟化;按照虚拟化程度可分为:全虚拟化、半虚拟化、硬件辅助虚拟化。 将不同的虚拟化对象和程度组合,可得出9种不同的服务器虚拟化技术。 首先详细介绍下服务器虚拟化之CPU虚拟化,后续讲解中再详细介绍内存虚拟化及I/O虚拟化。 CPU虚拟化 CPU全虚拟化技术 主要采用优先级压缩技术(Ring Compression)和二进制代码翻译技术(BinaryTranslation)。优先级压缩技术让VMM和Guest运行在不同的特权级下。对x86架构而言,即VMM运行在***特权级别Ring 0下,Guest OS运行在Ring 1下,用户应用运行在Ring 3下。因此,Guest OS的核心指令无法直接下达到计算机系统硬件执行,而是需要经过VMM的捕获和模拟执行(部分难以虚拟化的指令需要通过Binary Translation技术进行转换)。 CPU半虚拟化技术 主要采用Hypercall技术。Guest OS的部分代码被改变,从而使Guest OS会将和特权指令相关的操作都转换为发给VMM的Hypercall(超级调用),由VMM继续进行处理。而Hypercall支持的批处理和异步这两种优化方式,使得通过Hypercall能得到近似于物理机的速度。 CPU硬件辅助虚拟化技术 目前主要有Intel的VT-x和AMD的AMD-V这两种技术。其核心思想都是通过引入新的指令和运行模式,使VMM和Guest OS分别运行在不同模式(ROOT模式和非ROOT模式)下,且Guest OS运行在Ring 0下。通常情况下,Guest OS的核心指令可以直接下达到计算机系统硬件执行,而不需要经过VMM。当Guest OS执行到特殊指令的时候,系统会切换到VMM,让VMM来处理特殊指令。 CPU的虚拟化技术可以单CPU模拟多CPU并行,允许一个平台同时运行多个操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。 GuestOS负责第2级调度,即线程或进程在VCPU上的调度(将核心线程映射到相应的虚拟CPU上)。 VMM(Virtual Machine Monitor)负责第1级调度,即VCPU在物理处理单元上的调度。 两级调度的调度策略和机制不存在依赖关系。VMM负责物理处理器资源在各个虚拟机之间的分配与调度,本质上即把各个虚拟机中的VCPU按照一定的策略和机制调度在物理处理单元上可以采用任意的策略来分配物理资源,满足虚拟机的不同需求。

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

分布式基础,通俗易懂CAP?

分布式系统非常关注三个指标: ● 数据一致性 ● 系统可用性 ● 节点连通性与扩展性 关于一致性 数据“强一致性”,是希望系统只读到最新写入的数据,例如:通过单点串行化的方式,就能够达到这个效果。 关于session一致性,DB主从一致性,DB双主一致性,DB与Cache一致性,数据冗余一致性,消息时序一致性,分布式事务一致性,库存扣减一致性,详见文章《究竟啥才是互联网架构“一致性”》。 关于可用性 如果系统每运行100个时间单位,会有1个时间单位无法提供服务,则说系统的可用性是99%。 可用性和可靠性是比较容易搞混的两个指标,以一台取款机为例: ● 正确的输入,能够取到正确的钱,表示系统可靠 ● 取款机7*24小时提供服务,表示系统可用 保证系统高可用的方法是: ● 冗余 ● 故障自动转移 反向代理层,站点层,服务层,缓存层,数据库层各层保证系统高可用的方法,详见文章《究竟啥才是互联网架构“高可用”》。 关于连通性与扩展性 分布式系统,往往有多个节点,每个节点之间,都不是完全独立的,需要相互通信,当发生节点无法联通时,数据是否还能保持一致,系统要如何进行容错处理,是需要考虑的。 同时,连通性和扩展性紧密相关,想要加机器扩展性能,必须有良好的连通性。当一个节点脱离系统,系统就出现问题,往往意味着系统是无法扩展的。 反向代理层,站点层,服务层,缓存层,数据库层各层保证系统扩展性的方法,详见文章《究竟啥才是互联网架构“可扩展”》。 什么是CAP定理? CAP定理,是对上述分布式系统的三个特性,进行了归纳: ● 一致性( C onsistency) ● 可用性( A vailability) ● 分区容忍性( P artition Tolerance) 并且,定理指出,在系统实现时,这三者最多兼顾两点。 一致性,可用性,多节点扩展性三者只能取其二,既然加锁已经加上,常见的最佳工程架构实践是什么呢? 互联网,最常见的实践是这样的: ● 节点连通性,多节点扩展性,连通性异常的处理必须保证,满足P ● 一致性C与可用性A一般二选一 ● 选择一致性C,举例:传统单库水平切分,就是这类选型的典型 ● 选择可用性A,举例:双主库同步高可用,就是这类选型的典型 强一致很难怎么办? 单点串行化,虽然能保证“强一致”,但对系统的并发性能,以及高可用有较大影响,互联网的玩法,更多的是“最终一致性”,短期内未必读到最新的数据,但在一个可接受的时间窗口之后,能够读到最新的数据。 例如:数据库主从同步,从库上的数据,就是一个最终的一致。 总结 ● CAP可以理解为一致性,可用性,联通与扩展性 ● CAP三者只能取其二 ● 最常见的实践是AP+最终一致性 思路比结论重要。 原文发布时间为:2018-10-30 本文作者:58沈剑 本文来自云栖社区合作伙伴“架构师之路”,了解相关信息可以关注“架构师之路”。

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

Python基础系列-列表解析(list comprehension)

版权声明:如需转载,请注明转载地址。 https://blog.csdn.net/oJohnny123/article/details/81910866 列表解析(list comprehension) A2 = [i for i in A1 if i in A0] 其实等同于 A2 = [] for i in A1: if i in A0: A2.append(i) 所以就可以玩出很多花来了,包含列表的交集、差集等等。 代码: #!/usr/bin/python # -*- coding: UTF-8 -*- """ Created by liaoyangyang1 on 2018/2/28 下午9:26. """ A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) A1 = range(10) A2 = [i for i in A1 if i in A0] A3 = [A0[s] for s in A0] A4 = [i for i in A1 if i in A3] A5 = {i:i*i for i in A1} A6 = [[i,i*i] for i in A1] 执行结果: /Users/liaoyangyang/crc/codes-python/LearnPython/venv/bin/python /Users/liaoyangyang/crc/codes-python/LearnPython/test.py A0:{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4} A1:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] A2:[] A3:[1, 3, 2, 5, 4] A4:[1, 2, 3, 4, 5] A5:{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} A6:[[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]] Process finished with exit code 0

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

JavaScript 基础--- (运算符/数组/函数/变量)

基本概念: 1.javascript的组成:三部分组成 (1)ECMAScript - ECMA : 欧洲计算机协会 - 有ECMA组织制定的js的语法,语句..... (2)BOM:- broswer object model: 浏览器对象模型 (3)DOM:- document object model:文档对象模型 2. js的原始类型(五个) - string: 字符串 *** var str = "abc"; - number:数字类型 *** var m = 123; - boolean:true和false *** var flag = true; - null *** var date = new Date(); *** 获取对象的引用,null表示对象引用为空 ,所有对象的引用也是object - undifined *** 定义一个变量,没有赋值 *** var aa; ** typeof(); 查看当前变量的数据类型 3.js的运算符 ** += : x+=y; ===> x=x+y; ** js里面不区分整数和小数 var j = 123; alert(j/1000*1000); // j/1000*1000 在java里面得到结果是 0 // 在js里面不区分整数和小数,123/1000=0.123 * 1000 = 123 ** 字符串的相加和相减的操作 var str = "123"; ** 如果相加时候,做是字符串连接 ** 如果相减,做的是相减的运算 * //字符串的操作 var str = "456"; //alert(str+1); //在java里面操作的结果是 4561 ,在js里面还是 4561 alert(str-1); //相减时候,执行减法的运算 * 提示NaN:表示不是一个数字 ** boolean类型也可以操作 *** 如果设置成true,相当于这个值是1 *** 如果设置成false,相当于这个值是0 ** == 和 === 区别 ** 做判断** == 比较的只是值 === 比较的是值和类型 ** 引入知识 直接向页面输出的语句(可以把内容显示在页面上) * document.write("aaa"); document.wirte("<hr/>"); ** 可以向页面输出变量,固定值和html代码 4.实现99乘法表 5.js的数组 * 什么是数组? - 使用变量,var m = 10; - java里面的数组 定义 int[] arr = {1,2,3}; * 定义方式(三种) 第一种: var arr = [1,2,3]; var arr = [1,"4",true]; 第二种:使用内置对象 Array对象 var arr1 = new Array(5); //定义一个数组,数组的长度是5 arr1[0] = "1"; 第三种:使用内置对象 Array var arr2 = new Array(3,4,5); //定义一个数组,数组里面的元素是3 4 5 * 数组里面有一个属性 length:获取到数组的长度 * 数组可以存放不同的数据类型的数据 6.js的函数 ** 在java里面定义方法 public 返回类型void /int 方法名(参数列表) { 方法体; 返回值; } public int add(int a,int b) { int sum = a+b; return sum; } ** 在js里面定义函数(方法)有三种方式 **** 函数的参数列表里面,不需要写var,直接写参数名称 第一种方式: **** 使用到一个关键字 function **** function 方法名(参数列表) { 方法体; 返回值可有可无(根据实际需要); } // **** 代码:使用第一种方式创建函数 function test() { alert("qqqqq"); } //调用方法 //test(); //定义一个有参数的方法 实现两个数的相加 function add1(a,b) { var sum = a+b; alert(sum); } //add1(2,3); //有返回值的效果 function add2(a,b,c) { var sum1 = a+b+c; return sum1; } alert(add2(3,4,5)); 第二种方式: **** 匿名函数 var add = function(参数列表) { 方法体和返回值; } **** 代码 //第二种方式创建函数 var add3 = function(m,n) { alert(m+n); } //调用方法 add3(5,6); 第三种方式:(了解) *** 动态函数 *** 使用到js里面的一个内置对象 Function var add = new Function("参数列表","方法体和返回值"); 7.js的全局变量和局部变量 ** 全局变量:在script标签里面定义一个变量,这个变量在页面中js部分都可以使用(没有 Var 的也是全局变量) - 在方法外部使用,在方法内部使用,在另外一个script标签使用 var ss == 'zs'; //全局变量 ** 局部变量:在方法内部定义一个变量,只能在方法内部使用(写在function里面的) - 如果在方法的外部调用这个变量,提示出错 var ss = 'zs'; //局部变量

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

Java并发编程笔记之基础总结(二)

一.线程中断 Java 中线程中断是一种线程间协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是需要被中断的线程根据中断状态自行处理。 1.void interrupt() 方法:中断线程,例如当线程 A 运行时,线程 B 可以调用线程 A 的 interrupt() 方法来设置线程 A 的中断标志为 true 并立即返回。设置标志仅仅是设置标志,线程 A 并没有实际被中断,会继续往下执行的。如果线程 A 因为调用了 wait 系列函数或者 join 方法或者 sleep 函数而被阻塞挂起,这时候线程 B 调用了线程 A 的 interrupt() 方法,线程 A 会在调用这些方法的地方抛出 InterruptedException 异常而返回。 2.boolean isInterrupted():检测当前线程是否被中断,如果是返回 true,否者返回 false,代码如下: public boolean isInterrupted() { //传递false,说明不清除中断标志 return isInterrupted(false); } 3.boolean interrupted():检测当前线程是否被中断,如果是返回 true,否者返回 false,与 isInterrupted 不同的是该方法如果发现当前线程被中断后会清除中断标志,并且该函数是 static 方法,可以通过 Thread 类直接调用。另外从下面代码可以知道 interrupted() 内部是获取当前调用线程的中断标志而不是调用 interrupted() 方法的实例对象的中断标志。 public static boolean interrupted() { //清除中断标志 return currentThread().isInterrupted(true); } 下面看一个线程使用 Interrupted 优雅退出的经典使用例子,代码如下: public void run(){ try{ .... //线程退出条件 while(!Thread.currentThread().isInterrupted()&& more work to do){ // do more work; } }catch(InterruptedException e){ // thread was interrupted during sleep or wait } finally{ // cleanup, if required } } 下面看一个根据中断标志判断线程是否终止的例子: /** * Created by cong on 2018/7/17. */ public class InterruptTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { //如果当前线程被中断则退出循环 while (!Thread.currentThread().isInterrupted()) System.out.println(Thread.currentThread() + " hello"); } }); //启动子线程 thread.start(); //主线程休眠1s,以便中断前让子线程输出点东西 Thread.sleep(1); //中断子线程 System.out.println("main thread interrupt thread"); thread.interrupt(); //等待子线程执行完毕 thread.join(); System.out.println("main is over"); } } 运行结果如下: 如上代码子线程 thread 通过检查当前线程中断标志来控制是否退出循环,主线程在休眠 1s 后调用 thread 的 interrupt() 方法设置了中断标志,所以线程 thread 退出了循环。 总结:中断一个线程仅仅是设置了该线程的中断标志,也就是设置了线程里面的一个变量的值,本身是不能终止当前线程运行的,一般程序里面是检查这个标志的状态来判断是否需要终止当前线程。 二.理解线程上下文切换 在多线程编程中,线程个数一般都大于 CPU 个数,而每个 CPU 同一时刻只能被一个线程使用,为了让用户感觉多个线程是在同时执行,CPU 资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,在时间片内占用 CPU 执行任务。当前线程的时间片使用完毕后当前就会处于就绪状态并让出 CPU 让其它线程占用,这就是上下文切换,从当前线程的上下文切换到了其它线程。 那么就有一个问题让出 CPU 的线程等下次轮到自己占有 CPU 时候如何知道之前运行到哪里了? 所以在切换线程上下文时候需要保存当前线程的执行现场,当再次执行时候根据保存的执行现场信息恢复执行现场 线程上下文切换时机: 1.当前线程的 CPU 时间片使用完毕处于就绪状态时候; 2.当前线程被其它线程中断时候 总结:由于线程切换是有开销的,所以并不是开的线程越多越好,比如如果机器是4核心的,你开启了100个线程,那么同时执行的只有4个线程,这100个线程会来回切换线程上下文来共享这四个 CPU。 三.线程死锁 什么是线程死锁呢? 死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去。 如上图,线程 A 已经持有了资源1的同时还想要资源2,线程 B 在持有资源2的时候还想要资源1,所以线程1和线程2就相互等待对方已经持有的资源,就进入了死锁状态。 那么产生死锁的原因都有哪些,学过操作系统的应该都知道死锁的产生必须具备以下四个必要条件。 1.互斥条件:指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其它进行请求获取该资源,则请求者只能等待,直至占有资源的线程用毕释放。 2.请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已被其其它线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源。 3.不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其它线程抢占,只有在自己使用完毕后由自己释放。 4.环路等待条件:指在发生死锁时,必然存在一个线程——资源的环形链,即线程集合{T0,T1,T2,···,Tn}中的 T0 正在等待一个 T1 占用的资源;T1 正在等待 T2 占用的资源,……Tn正在等待已被 T0 占用的资源。 下面通过一个例子来说明线程死锁,代码如下: /** * Created by cong on 2018/7/17. */ public class DeadLockTest1 { // 创建资源 private static Object resourceA = new Object(); private static Object resourceB = new Object(); public static void main(String[] args) { // 创建线程A Thread threadA = new Thread(new Runnable() { public void run() { synchronized (resourceA) { System.out.println(Thread.currentThread() + " get ResourceA"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get ResourceB"); synchronized (resourceB) { System.out.println(Thread.currentThread() + "get ResourceB"); } } } }); // 创建线程B Thread threadB = new Thread(new Runnable() { public void run() { synchronized (resourceB) { System.out.println(Thread.currentThread() + " get ResourceB"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get ResourceA"); synchronized (resourceA) { System.out.println(Thread.currentThread() + "get ResourceA"); } }; } }); // 启动线程 threadA.start(); threadB.start(); } } 运行结果如下: 下面分析下代码和结果,其中 Thread-0 是线程 A,Thread-1 是线程 B,代码首先创建了两个资源,并创建了两个线程。 从输出结果可以知道线程调度器先调度了线程 A,也就是把 CPU 资源让给了线程 A,线程 A 调用了 getResourceA() 方法,方法里面使用 synchronized(resourceA) 方法获取到了 resourceA 的监视器锁,然后调用 sleep 函数休眠 1s,休眠 1s 是为了保证线程 A 在执行 getResourceB 方法前让线程 B 抢占到 CPU 执行 getResourceB 方法。 线程 A 调用了 sleep 期间,线程 B 会执行 getResourceB 方法里面的 synchronized(resourceB),代表线程 B 获取到了 objectB 对象的监视器锁资源,然后调用 sleep 函数休眠 1S。 好了,到了这里线程 A 获取到了 objectA 的资源,线程 B 获取到了 objectB 的资源。线程 A 休眠结束后会调用 getResouceB 方法企图获取到 ojbectB 的资源,而 ObjectB 资源被线程 B 所持有,所以线程 A 会被阻塞而等待。而同时线程 B 休眠结束后会调用 getResourceA 方法企图获取到 objectA 上的资源,而资源 objectA 已经被线程 A 持有,所以线程 A 和 B 就陷入了相互等待的状态也就产生了死锁。 下面从产生死锁的四个条件来谈谈本案例如何满足了四个条件。 首先资源 resourceA 和 resourceB 都是互斥资源,当线程 A 调用 synchronized(resourceA) 获取到 resourceA 上的监视器锁后释放前,线程 B 在调用 synchronized(resourceA) 尝试获取该资源会被阻塞,只有线程 A 主动释放该锁,线程 B 才能获得,这满足了资源互斥条件。 线程 A 首先通过 synchronized(resourceA) 获取到 resourceA 上的监视器锁资源,然后通过 synchronized(resourceB) 等待获取到 resourceB 上的监视器锁资源,这就构造了持有并等待。 线程 A 在获取 resourceA 上的监视器锁资源后,不会被线程 B 掠夺走,只有线程 A 自己主动释放 resourceA 的资源时候,才会放弃对该资源的持有权,这构造了资源的不可剥夺条件。 线程 A 持有 objectA 资源并等待获取 objectB 资源,而线程 B 持有 objectB 资源并等待 objectA 资源,这构成了循环等待条件。 所以线程 A 和 B 就形成了死锁状态。 那么如何避免线程死锁呢? 要想避免死锁,需要破坏构造死锁必要条件的至少一个即可,但是学过操作系统童鞋应该都知道目前只有持有并等待和循环等待是可以被破坏的。 造成死锁的原因其实和申请资源的顺序有很大关系,使用资源申请的有序性原则就可以避免死锁,那么什么是资源的有序性呢,先看一下对上面代码的修改: // 创建线程B Thread threadB = new Thread(new Runnable() { public void run() { synchronized (resourceA) { System.out.println(Thread.currentThread() + " get ResourceB"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get ResourceA"); synchronized (resourceB) { System.out.println(Thread.currentThread() + "get ResourceA"); } }; } }); 运行结果如下: 如上代码可知修改了线程 B 中获取资源的顺序和线程 A 中获取资源顺序一致,其实资源分配有序性就是指假如线程 A 和 B 都需要资源1,2,3……n 时候,对资源进行排序,线程 A 和 B 只有在获取到资源 n-1 时候才能去获取资源 n。 总结:编写并发程序,多个线程进行共享多个资源时候要注意采用资源有序分配法避免死锁的产生。 四守护线程与用户线程 Java 中线程分为两类,分别为 Daemon 线程(守护线程)和 User 线程(用户线程),在 JVM 启动时候会调用 main 函数,main 函数所在的线程是一个用户线程,这个是我们可以看到的线程,其实 JVM 内部同时还启动了好多守护线程,比如垃圾回收线程(严格说属于 JVM 线程)。 那么守护线程和用户线程有什么区别呢? 区别之一是当最后一个非守护线程结束时候,JVM 会正常退出,而不管当前是否有守护线程;也就是说守护线程是否结束并不影响 JVM 的退出。言外之意是只要有一个用户线程还没结束正常情况下 JVM 就不会退出。 那么 Java 中如何创建一个守护线程呢?代码如下: public static void main(String[] args) { Thread daemonThread = new Thread(new Runnable() { public void run() { } }); //设置为守护线程 daemonThread.setDaemon(true); daemonThread.start(); } 可知只需要设置线程的 daemon 参数为 true 即可。 下面通过例子来加深用户线程与守护线程的区别的理解,首先看下面代码: /** * Created by cong on 2018/7/17. */ public class UserThreadTest { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { public void run() { for(;;){} } }); //启动子线 thread.start(); System.out.print("main thread is over"); } } 运行结果如下: 如上代码在 main 线程中创建了一个 thread 线程,thread 线程里面是无限循环,运行代码从结果看 main 线程已经运行结束了,那么 JVM 进程已经退出了?从 IDE 的输出结侧上的红色方块说明 JVM 进程并没有退出,另外 Mac 上执行ps -eaf | grep java会输出结果,也可以证明这个结论。 这个结果说明了当父线程结束后,子线程还是可以继续存在的,也就是子线程的生命周期并不受父线程的影响。也说明了当用户线程还存在的情况下 JVM 进程并不会终止。 那么我们把上面的 thread 线程设置为守护线程后在运行看看会有什么效果,代码如下: /** * Created by cong on 2018/7/17. */ public class DaemonThreadTest { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { public void run() { for(;;){} } }); //设置为守护线程 thread.setDaemon(true); //启动子线 thread.start(); System.out.print("main thread is over"); } } 运行结果如下: 如上在启动线程前设置线程为守护线程,从输出结果可知 JVM 进程已经终止了,执行ps -eaf |grep java也看不到 JVM 进程了。这个例子里面 main 函数是唯一的用户线程,thread 线程是守护线程,当 main 线程运行结束后,JVM 发现当前已经没有用户线程了,就会终止 JVM 进程。 Java 中在 main 线程运行结束后,JVM 会自动启动一个叫做 DestroyJavaVM 线程,该线程会等待所有用户线程结束后终止 JVM 进程。 下面通过简单的 JVM 代码来证明这个结论,翻开 JVM 的代码,最终会调用到 JavaMain 这个函数: int JNICALL JavaMain(void * _args) { ... //执行Java中的main函数 (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); //main函数返回值 ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; //等待所有非守护线程结束,然后销毁JVM进程 LEAVE(); } LEAVE 是 C 语言里面的一个宏定义,定义如下: #define LEAVE() do { if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { JLI_ReportErrorMessage(JVM_ERROR2); ret = 1; } if (JNI_TRUE) { (*vm)->DestroyJavaVM(vm); return ret; } } while (JNI_FALSE) 上面宏的作用实际是创建了一个名字叫做 DestroyJavaVM 的线程来等待所有用户线程结束。 在 Tomcat 的 NIO 实现 NioEndpoint 中会开启一组接受线程用来接受用户的链接请求和一组处理线程负责具体处理用户请求,那么这些线程是用户线程还是守护线程呢?下面我们看下 NioEndpoint 的 startInternal 方法,源码如下: public void startInternal() throws Exception { if (!running) { running = true; paused = false; ... //创建处理线程 pollers = new Poller[getPollerThreadCount()]; for (int i=0; i<pollers.length; i++) { pollers[i] = new Poller(); Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i); pollerThread.setPriority(threadPriority); pollerThread.setDaemon(true);//声明为守护线程 pollerThread.start(); } //启动接受线程 startAcceptorThreads(); } protected final void startAcceptorThreads() { int count = getAcceptorThreadCount(); acceptors = new Acceptor[count]; for (int i = 0; i < count; i++) { acceptors[i] = createAcceptor(); String threadName = getName() + "-Acceptor-" + i; acceptors[i].setThreadName(threadName); Thread t = new Thread(acceptors[i], threadName); t.setPriority(getAcceptorThreadPriority()); t.setDaemon(getDaemon());//设置是否为守护线程,默认为守护线程 t.start(); } } private boolean daemon = true; public void setDaemon(boolean b) { daemon = b; } public boolean getDaemon() { return daemon; } 如上代码也就是说默认情况下接受线程和处理线程都是守护线程,这意味着当 Tomact 收到 shutdown 命令后 Tomact 进程会马上消亡,而不会等处理线程处理完当前的请求。 总结:如果你想在主线程结束后 JVM 进程马上结束,那么创建线程的时候可以设置线程为守护线程,否则如果希望主线程结束后子线程继续工作,等子线程结束后在让 JVM 进程结束那么就设置子线程为用户线程。

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

Android 基础动画之补间动画详解

Android系统SDK为开发者提供了很多丰富的API去实现绚丽夺目的动画,动画也是学习掌握自定义控件的必不可少的内容。Android动画主要分为如下几类: View Animation: 视图动画(也叫补间动画:Tween Animation)在Android早期版本系统中就已经提供了,这种动画只能被用来设置View的动画。 Drawable Animation: 一般称为Frame动画、帧动画,这类动画可以划分到视图动画的类别,专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。 Property Animation: 属性动画,属性动画只对Android 3.0(API 11)以上版本的Android系统才有效, 这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以让你自定义任何类型和属性的动画。 下面就以上动画分类逐个分析(介于篇幅的原因我将动画分类写成不同的文章这样方便阅读): View Animation(补间动画): 补间动画可以在一个视图容器内执行一系列简单变换(具体的变换步骤有:位置、大小、旋转、透明度)。 假设现在有一个View对象,我们可以通过平移、旋转、缩放、透明度等API进行具体的操作。 补间动画的实现方式可以通过 XML或通过Android代码两种方式 去定义。使用XML文件这种编写方式去定义补间动画可能会更加快捷方便(因为XML本质就是一门标记语言、可读性强)。 另外,说到补间动画,就必须要提到Animation抽象类,Animation抽象类是所有补间动画类的基类,那么作为父类的Animation肯定提供了一些公共的属性供子类去使用: Animation常用属性 Animation抽象类的子类补间动画常见有以下四个子类,其子类对应的XML写法以及类信息如下图: 具体的补间动画 A:平移动画Translation xml方式实现 文件名:animator_translate.xml <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:fromYDelta="0" android:toYDelta="0" android:toXDelta="200" android:duration="500" android:fillAfter="true"> </translate> 代码加载xml文件获取动画 //加载动画 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_translate); //执行动画 testBtn.startAnimation(animation); 代码方式实现 TranslateAnimation translateAnimation = new TranslateAnimation(0,200,0,0); translateAnimation.setDuration(500);//动画执行时间 translateAnimation.setFillAfter(true);//动画执行完成后保持状态 //执行动画 testBtn.startAnimation(translateAnimation); 以上是平移动画的两种实现方式。平移动画需要注意的一些属性有: android:fromXDelta对应的就是TranslateAnimation(float fromXDelta, …) 起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) ;android:fromYDelta对应的就是TranslateAnimation(…, float fromYDelta, …) 起始点Y轴从标,平移的规律同上 ;android:toXDelta对应的就是TranslateAnimation(…, float toXDelta, …) 结束点X轴坐标,平移的规律同上 ;android:toYDelta对应的就是TranslateAnimation(…, float toYDelta) 结束点Y轴坐标,平移的规律同上 B:旋转动画Rotation xml方式实现 文件名:animator_rotation.xml <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="90" android:pivotX="50%" android:pivotY="50%" android:duration="500" android:fillAfter="true"> </rotate> 代码加载xml文件获取动画 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_rotation); testBtn.startAnimation(animation); 代码方式实现 RotateAnimation rotateAnimation = new RotateAnimation(0,90,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); testBtn.startAnimation(rotateAnimation); 以上是旋转动画的两种实现方式。旋转动画需要注意的一些属性有: android:fromDegrees对应的就是(RotateAnimation(float fromDegrees, …))这个属性代表的是:旋转开始角度,正代表顺时针度数,负代表逆时针度数 ;android:toDegrees对应的就是(RotateAnimation(…, float toDegrees, …))这个属性代表的是:旋转结束角度,正代表顺时针度数,负代表逆时针度数 ;android:pivotX对应的就是(RotateAnimation(…, float pivotX, …))这个属性代表的是:缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) ;android:pivotY对应的就是(RotateAnimation(…, float pivotY))这个属性代表的是:缩放起点Y坐标,同上规律 C、缩放动画Scale xml方式实现 文件名:animator_scal.xml <?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:fromXScale="1" android:fromYScale="1" android:toYScale="2" android:toXScale="2" android:duration="500" android:pivotX="50%" android:pivotY="50%" android:fillAfter="true"> </scale> 代码加载xml文件获取动画 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_scal); testBtn.startAnimation(animation); 代码方式实现 ScaleAnimation scaleAnimation = new ScaleAnimation(1,2,1,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); scaleAnimation.setDuration(500); scaleAnimation.setFillAfter(true); testBtn.startAnimation(scaleAnimation); 以上是缩放动画的两种实现方式。缩放动画需要注意的一些属性有: android:fromXScale对应的就是(ScaleAnimation(float fromX, …)这个属性代表的是:初始X轴缩放比例,1.0表示无变化 ;android:toXScale对应的就是(ScaleAnimation(…, float toX, …)这个属性代表的是:结束X轴缩放比例 ;android:fromYScale对应的就是(ScaleAnimation(…, float fromY, …)这个属性代表的是:初始Y轴缩放比例 ;android:toYScale对应的就是(ScaleAnimation(…, float toY, …)这个属性代表的是:结束Y轴缩放比例 ;android:pivotX对应的就是(ScaleAnimation(…, float pivotX, …)这个属性代表的是:缩放起点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) ;android:pivotY对应的就是(ScaleAnimation(…, float pivotY)这个属性代表的是:缩放起点Y轴坐标,同上规律 D:透明度动画Alpha xml方式实现 文件名:animator_alpha.xml <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:fromAlpha="1" android:toAlpha="0.2" android:duration="500" android:fillAfter="true"> </alpha> 代码加载xml文件获取动画 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_alpha); testBtn.startAnimation(animation); 代码方式实现 AlphaAnimation alphaAnimation = new AlphaAnimation(1,0.2f); alphaAnimation.setDuration(500); alphaAnimation.setFillAfter(true); testBtn.startAnimation(alphaAnimation); 以上是透明度动画的两种实现方式。透明度动画需要注意的是有个属性:android:fromAlpha对应的就是(AlphaAnimation(float fromAlpha, …) )这个属性代表的是:动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明) android:toAlpha对应的就是(AlphaAnimation(…, float toAlpha))这个属性代表的是:动画结束的透明度(0.0到1.0,0.0是全透明,1.0是不透明) AnimationSet动画集合的方式实现 (伪代码): 文件名:aset <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" > <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float" /> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float" /> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float" /> </set> Java代码实现: ImageView view = (ImageView) findViewById(R.id.image); Animation animationSet = AnimationUtils.loadAnimation(this, R.anim.aset); //一些拓展API //开始动画集 animationSet.start(); //取消动画集 animationSet.cancel(); //判断当前动画集是否开始 animationSet.hasStarted(); //判断当前动画集是否结束 animationSet.hasEnded(); //重新开始当前动画集 animationSet.reset(); view.startAnimation(animationSet); 值得注意的是:补间动画执行之后并未改变View的真实布局属性。假设现在Activity中有一个 Button在屏幕上方,设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方。如果点击屏幕下方动画执行之后的Button是没有任何反应,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件,这一点是需要注意的。 这个问题是基于Android早期团队设计的时候带来的一个问题,那么谷歌Android团队后期针对这个问题肯定提供了解决方案...... 未完待续!!! 如果这篇文章对你有帮助,希望各位看官留下宝贵的star,谢谢。 Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果

资源下载

更多资源
Mario

Mario

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

Spring

Spring

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

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册