Java 多线程

多线程 线程是独立的执行空间,Java语言内置多线程功能,用类Thread来表达。每个Java应用程序会启动一个主线程 – 将main()放在自己的执行空间的最开始处. JVM会负责主线程的启动(以及其他比如GC的系统线程). 程序员负责启动自己的建立的线程. 启动新线程 建立Runnable对象作为线程任务Runnable job = new MyRunnable(), Runnable接口只有一个方法run() 建立Thread对象并赋值Runnable Thread thread1 = new Thread(job); Thread thread2 = new Thread(job) 启动thread.start(); 另一种创建线程的方法是用Thread的子类覆盖掉run(), 构造新线程Thread t = new Thread();. 从OO的角度看待, 此时Thread 与线程任务是不同概念的. 让子类继承Thread的目的通常是需要更特殊的Thread, 需要特殊的行为, 如果没有这种需求, 就没必要继承Thread. 线程调度器 多线程间的切换由调度器scheduler来管理, 线程有多种状态: 执行中, sleep(2000): 睡眠2000ms, 时间到之前不会被执行, 但时间到了并不保证一定会被执行. 可能会抛出InterruptedException, 所以对它的调用要包含在try/catch中. locked. 线程的run完成执行后, 将无法重新启动. 调度器在不同的JVM有不同的做法. 测试多线程时需要在不同机器上测试. 并发 Concurrency并发环境中, 为了避免冲突, 需要上锁, 使用synchronized来修饰方法使之每次只能被单一线程读写. 同步化是有代价的, 查询钥匙有性能上的损耗, 同步化也会强制线程排队执行, 还可能出现死锁. 死锁 因为两个线程互相持有对方正在等待的东西, 导致没有一方可以脱离等待. 数据库有事务回滚机制来复原死锁的事务, 但Java没有处理死锁的机制. ...

2017-02-26 · 1 min · Cong Chan

Java 套接字Socket

Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,确切的说Socket是个代表两台机器之间网络连接的对象。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。 Socket socket = new Socket("192.168.2.1", 5000), 第一个参数是IP地址, 第二个参数是端口. Socket连接的建立代表两台机器之间存有对方的信息, 包括网络地址和TCP端口号. 但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。 使用BufferedReader从Socket上读取数据: 建立对服务器的Socket连接Socket socket = new Socket("192.168.2.1", 5000) 建立连接到Socket上低层输入串流的InputStreamReader InputStreamReader stream = new InputStreamReader(socket.getInputStream());, 作为从低层和高层串流间的桥梁, 把来自服务器的字节转换为字符 建立缓冲区BufferedReader来读取 BufferedReader reader = new BufferedReader(stream); String msg = reader.readLine(); 用PrintWriter写数据到Socket上: 建立对服务器的Socket连接 建立链接到Socket的PrintWriter PrintWriter writer = new PrintWriter(socket.getOutputStream()), 作为字符数据和字节间的转换桥梁, 可以衔接Strings和Socket两端 写入数据 writer.println("You have a message"); TCP/IP TCP/IP协议集包括应用层, 传输层,网络层,网络访问层。 应用层协议主要包括如下几个:FTP、TELNET、DNS、SMTP、NFS、HTTP。 FTP(File Transfer Protocol)是文件传输协议,一般上传下载用FTP服务,数据端口是20H,控制端口是21H。 Telnet服务是用户远程登录服务,使用23H端口,使用明码传送,保密性差、简单方便。 DNS(Domain Name Service)是域名解析服务,提供域名到IP地址之间的转换,使用端口53。 SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,用来控制信件的发送、中转,使用端口25。 NFS(Network File System)是网络文件系统,用于网络中不同主机间的文件共享。 HTTP(Hypertext Transfer Protocol)是超文本传输协议,用于实现互联网中的WWW服务,使用端口80。 网络接口层: ...

2017-02-26 · 2 min · Cong Chan

Java 抽象类

抽象类 有些情况下,有些父类在实际应用中只有被继承的和产生多态的意义,而没有实例化的意义(比如抽象的Animal, Canine等,实例化这些抽象概念没有实际意义),所以不希望这种父类会被初始化。通过标记类为抽象类,编译器就知道该类不能创建实例(不能作为new实例, 但可以用于声明类型). abstract class Canine extends Animal { ... } Canine c = new Dog; // Canine c = new Canine(); 无法编译 反之,不抽象的类就是具体类. 有抽象类自然就有抽象方法,抽象的方法没有实体,所有就不会含有具体的实现public abstract void doSomething();. 只有抽象类才能拥有抽象方法. 在抽象父类中定义可继承的抽象方法, 可以定义出一组子类共同的协议, 这样能够保证多态. 但因为抽象方法只是为了标记处多态而存在, 它们没有具体的内容, 这样在继承树结构下的第一个具体类就必须要实现所有的抽象方法. 当然, 树结构中的抽象类也可以提前把抽象方法实现了(default方法). 抽象类和接口的比较 接口是抽象类的极端形式,接口是完全抽象的,不包含实现(新的特性支持default method)。 有很多情景需要某个类继承多种父类。比如,因为原来的业务需求比较单一,只需要Animal - Canine - Dog这种类结构就满足需求了, 此时Dog只是Animal的子类. 但后来有了新的功能需求, 上线了宠物功能, 理论上可以为每一种具体的属于宠物的子类添加宠物功能, 这就涉及大量的人工和bug. 但假如额外设计一种Pet类, 那么Pet和Animal会有交叉重叠, 如果让宠物子类同时继承两种超类, 那就是多重继承. 因为多重继承会有致命方块的问题. 所以Java不支持这种方式. 而接口这个概念, 就是用于解决这个问题的: public interface Pet { public abstract void beFriendly(); public abstract void play(); } // 对于属于宠物的子类,让其实现接口`Pet`. public class Dog extends Canine implements Pet {} 基本上,接口能做的抽象类都可以做。 但接口的最大意义就是其无比的适用性, 若以接口取代具体的子类或抽象类作为参数或返回类型, 那么就可以传入任何有实现该接口的东西. 有了接口, ...

2017-02-26 · 2 min · Cong Chan

Java 类的继承扩展 Extends

类的继承扩展 定义class之间的层次关系. 假设要构建一个RotatingSLList,它具有与SLList相同的功能,如addFirst, size等,但是需要额外的rotateRight操作将最后一项放到列表的前面,因为继承允许子类重用已经定义的类中的代码。所以让RotatingSLList类从SLList继承部分代码: public class RotatingSLList<Item> extends SLList<Item> {} RotatingSLList“是一种”SLList, extends可以让我们继承SLList的原始功能,并能修改或添加其他功能。 /** The rotateRight method takes in an existing list, and rotates every element one spot to the right, moving the last item to the front of the list. For example, input [5, 9, 15, 22] should return [22, 5, 9, 15]. */ public void rotateRight() { Item x = removeLast(); addFirst(x); } 通过extends, 子类继承父类的所有成员,成员包括: ...

2017-02-25 · 4 min · Cong Chan

Java 13 | 接口默认方法

除了单纯提供声明之外,Java 8 也允许接口提供具体的实现方法。 缺省方法 从 Java 8开始支持 Default method。 我们可以在List中列出已实现的method。这些方法就是 default method,定义了List hypernyms的一些默认行为:default public void method() { ... }. 我们可以自由调用interface中定义的方法,而不用操心具体的实现。Default method 适用于实现接口的任何类型的对象!子类可以直接调用,而不必重新实现 default method。 // List default public void print() { ... } 不过,我们仍然可以override default method,在子类中重新定义该方法。这样,只要我们在LinkedLList上调用print(),它就会调用子类override的方案,而不是父类的。 // LinkedList @Override public void print() { ... } Dynamic type Java是通过一个叫“dynamic method selection”的特性,来确定要调用 default method 还是已经被子类override的method。 当实例声明List<String> l = new LinkedList<String>();, 则指明l是 static 类型的 List。由 new 生成的 object 是LinkedList类型,也从属于 List 类型。但是,因为这个对象本身是使用 LinkedList 构造函数实例化的,所以我们称之为 dynamic type。 ...

2017-02-24 · 1 min · Cong Chan

Java 12 | 接口 Interface

子类在什么情况下需要多个父类? 比如,因为原来的业务需求比较单一,只需要Animal - Canine - Dog这种类结构就满足需求了, 此时Dog只是Animal的子类. 但后来有了新的功能需求, 上线了宠物功能, 理论上可以为每一种具体的属于宠物的子类添加宠物功能, 这就涉及大量的人工和bug. 但假如额外设计一种Pet类, 那么Pet和Animal会有交叉重叠, 如果让宠物子类同时继承两种超类, 那就是多重继承. 因为多重继承会有致命方块的问题, 不同父类对同一个方法的可能有不同的实现方式, 这会导致冲突. 所以Java不支持这种方式. 接口 而接口这个概念, 就可以用于解决这个问题的. 类不需要继承多个父类, 只需要实现一个或多个接口指定的所有方法/行为的关系. public interface Pet { public abstract void beFriendly(); public abstract void play(); } // 对于属于宠物的子类,让其实现接口`Pet`. public class Dog extends Canine implements Pet {} 接口作为参数 我们前面创建的 LinkedList and ArrayList 其实很相似 - 所有的method都一样. 如果我们需要写一个需要用到数组的类比如WordUtils class, public class WordUtils { /** Returns the length of the longest word. */ public static String longest(LinkedList<String> list) { ... return list.get(maxDex); } } 我们如何让longest方法也适配ArrayList?简单的方法及时写两个同名不同参数的methods。即所谓method overloading。 public static String longest(LinkedList<String> list) public static String longest(ArrayList<String> list) ...

2017-02-23 · 2 min · Cong Chan

Python之奇技淫巧

FBI WARNING 这不是python入门 函数 Fundamentally, the qualities of good functions all reinforce the idea that functions are abstractions. 函数作为一种机制, 提供了用于抽象数值运算的模式, 使其独立于所涉及的特定值。 文档 code is written only once, but often read many times. docstring def pressure(v, t, n): """Compute the pressure in pascals of an ideal gas. Applies the ideal gas law: http://en.wikipedia.org/wiki/Ideal_gas_law v -- volume of gas, in cubic meters t -- absolute temperature in degrees kelvin n -- particles of gas """ >>> help(pressure) Python docstring guidelines ...

2017-02-22 · 2 min · Cong Chan

Java 11 | 测试 Testing

测试 如何知道自己的程序是否真的在工作?在现实世界中,程序员相信他们的代码,因为代码通过了他们自己编写的测试。常用的测试有 Ad Hoc Testing, Unit test 和 Integration Testing。 Ad Hoc Testing,是指没有计划和记录的软件测试,除非发现缺陷,不然一般只运行一次。 Unit test 程序可分解为单元(或程序中可测试的最小部分),Unit test 严格测试代码的每个单元,最终确保项目正确运行。 Unit test 好处: Unit test 保证良好的代码结构(每个 method “只打一份工”),帮助我们较好地解析任务, 允许我们考虑每个方法的所有边界情况,并单独测试它们。 让我们每次只专注于一个单元,进行测试,debug,对准确度有信心后,再进行下一个单元的开发。相比于一次性写完所有代码,再测试debug,Unit test 减少了 debugging 时间。 坏处: 测试也要花时间 测试本身也是有可能出错的,测试可能不全面,不规范,或者有bug 有些单元是依赖于其他单元的 Unit testing 无法保证各个模块的交互,无法保证整个系统作为一个整体是否正常工作。 JUnit JUnit是一个给Java做测试的框架,由Erich Gamma(Design Patterns)和Kent Beck(eXtreme Programming)编写。 JUnit使用Java的 reflection 功能(Java程序可以检查自己的代码)和注释。 JUnit允许我们: 定义并执行测试和测试套件 使用测试作为规范的有效手段 使用测试来支持重构 将修改的代码集成到构建中 JUnit可用于多个IDE,例如BlueJ,JBuilder和Eclipse在一定程度上具有JUnit集成。 import org.junit.Test; import static org.junit.Assert.*; @Test public void testMethod() { assertEquals(<expected>, <actual>); } assertEquals测试一个变量的实际值是否等于它的期望值。 JUnit test 各个测试方法,必须是非静态的(JUnit的设计人员设计规定的)。 ...

2017-01-29 · 1 min · Cong Chan

Java 10 | 数据结构 - LinkedList 还是 ArrayList

Java 提供了 ArrayList, ArrayDeque 和 LinkedList 几个API. 队列 queue, 通俗的含义, 就是不能插队, 只能在末尾插入. 双端队列 Double Ended Queue (Deque) 是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩 –http://www.cplusplus.com/reference/deque/deque/ CS61b的project 1a需要实现两种双端队列(array based 和 linkedklist based). 不同的API, 在考虑什么时候应该用哪个时, 我们需要考虑它们的性能差异: 搜索/定位:与LinkedList相比,ArrayList搜索更快。 ArrayList的get(int index)性能是O(1)的,而LinkedList的性能是O(n)。因为ArrayList基于array数据结构,可以直接用 array index 定位元素。 删除/插入:LinkedList 操作性能是O(1),而ArrayList的性能从O(n)(删除/插入第一个元素)到O(n)(最后一个元素)都有可能。因为LinkedList的每个元素都包含两个指向其相邻前后元素的指针(地址),因此仅需要改变,被删节点的prev和next指针位置。而在ArrayList中,需要移动剩余元素,来重新填充array空间。 内存开销:LinkedList的每个元素都有更多的内存开销(额外的指针), 而ArrayLists没有这个开销。但是,ArrayLists需要占用初始容量。一般ArrayList的默认初始容量非常小(Java 1.4 - 1.8使用10)。但是,往ArrayLists添加元素时, 它可能会适当地增大容量,所以如果添加了很多元素,则必须不断调整数组的大小,那样也可能会导致元素频繁挪动位置。 综上所述: 如果在应用中需要频繁插入和删除,那么选择LinkedList。 假如一开始,就知道后面要添加大量元素,那就使用较高的初始容量来构造ArrayList。 大部分用例中, 相比LinkedList, 人们更偏爱ArrayList以及ArrayDeque。如果你不确定应该选哪个, 那么就直接考虑ArrayList吧(参考 https://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist).

2017-01-28 · 1 min · Cong Chan

Java 09 | 数据结构 - 双向链表 Doubly Linked List

双向链表 Doubly Linked List 前面介绍过的单向链表有几个缺点. 第一个就是它的addLast操作非常慢。单向链表只有一个变量保存列表头的地址, 以及每个节点对后面节点的单向引用(链接). 对于很长的列表,addLast方法必须遍历整个列表, 直到找到列表末尾才能执行插入操作. 最直观的优化方案就是加个’车尾’ 这样我们就可以直接通过last.next引用末尾位置. 不过另一个问题并没有解决, 就是删除列表最后一项removeLast这个操作还是很慢。因为在目前的结构设计下, 我们需要先找到倒数第二项,然后将其下一个指针设置为null。而要找到倒数第二节点, 我们就得先找到倒数第三个节点…… 以此类推。也就是说,对于删除末尾的操作,还是要几乎遍历整个列表。 反方向的链接 基于前面单向链表构建双向链表, 一个比较有效的方法是额外为每个节点添加一个指向前面节点的链接 - 指针. public class OneNode { public OneNode prev; //指向前 public int item; public OneNode next; //指向后 } 增加这些额外的指针会导致额外的代码复杂度, 以及额外的内存开销, 这就是追求时间效率的代价. Sentinel 与尾节点 双向链表的一个设计初衷,就是为了解决单向链表针对列表末尾位置的操作效率不高的问题,除了sentinel和反方向的链接还不够,我们还需要一个节点(指针)能够直接帮我们定位到列表末端。可以考虑添加一个的尾节点last 这样的列表就可以支持O(1)复杂度的addLast,getLast 和 removeLast操作了。 循环双端队列 Circular double ended queue 上面的尾节点设计虽然没什么错误,但有点瑕疵:最后一个尾节点指针有时指向前哨节点,有时指向一个真正的节点。更好的方法是使双向链表首尾相连, 构成一个循环,即前后节点共享唯一的一个前哨节点。 这样的设计相对更整洁,更美观(主观上的), sentinel的prev就指向列表最后一个节点, sentinel的next指向列表第一个节点. ...

2017-01-13 · 2 min · Cong Chan