注意:TCP 并不能保证数据一定会被对方接收到,因为这是不可能的。TCP 能够做到的是,如果有可能,就把数据递送到接收方,否则就(通过放弃重传并且中断连接这一手段)通知用户。因此准确说 TCP 也不是 100% 可靠的协议,它所能提供的是数据的可靠递送或故障的可靠通知。
UDP 是一个简单的传输层协议。和 TCP 相比,UDP 有下面几个显著特性:
总结: 基于连接vs无连接,可靠性不同,有序性,数据边界,速度,重量级vs轻量级
UDP更适用于对速度比较敏感的应用,例如:在线视频媒体,电视广播和多人在线游戏。
基于TCP协议的最好例子是HTTP协议和HTTPS协议,他们几乎存在于互联网的任何地方,实际上,绝大多数你所熟悉的通常协议,都是基于TCP的,例如:Telnet,FTP以及SMTP协议。UDP协议没有TCP协议那么受欢迎,但是也被广泛应用,比如DHCP以及DNS协议,其他还有一些基于UDP的协议如SNMP,TFTP,BOOTP以及NFS(早期版本)
多进程
多线程
同步与异步:在计算机领域,同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。举个例子,打电话时就是同步通信,发短息时就是异步通信。
进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。进程是cpu资源分配的最小单位,线程是cpu调度的最小单位
并行和并发:并行处理(Parallel Processing)是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面。并行处理的主要目的是节省大型和复杂问题的解决时间。并发处理(concurrency Processing):指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机(CPU)上运行。并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说,并行是并发的子集
Thread是类,Runnable是接口,继承Thread类和实现Runnable接口都能实现多线程。由于Java不支持多继承,因此继承Thread类就代表这个子类就不能继承其他类,而实现Runnable接口没有这个限制。其实Thread类本身也是实现了Runnable接口。
当使用Runnable接口时,我们还是需要实例化一个Thread类并传入Runnable的实例来调用start()方法
public class RunnableExample implements Runnable{
public void run(){
...
}
}
public class ThreadExample extends Thread{
public void run(){
...
}
}
public static void main(String[] args){
// 实现Runnable接口
RunnableExample instance = new RunnableExample();
Thread thread = new Thread(instance);
thread.start();
// 继承Thread类
ThreadExample thread2 = new ThreadExample();
thread2.start();
}
sleep()方法正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行(注意:sleep方法只让出了CPU,而并不会释放同步资源锁);
wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。(注意:notify的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说notify只是让之前调用wait的线程有权利重新参与线程的调度);
sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;
CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。如下:
public static class MyLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public boolean lock() {
return locked.compareAndSet(false, true);
}
}
注意这个locked是同步的,在某一时刻只能有一个线程在同一个MyLock实例上改变它的值。AtomicBoolean类中有一个compareAndSet()方法,它使用一个期望值和AtomicBoolean实例的值比较,和两者相等,则使用一个新值替换原来的值。在这个例子中,它比较locked的值和false,如果locked的值为false,则把修改为true。如果值被替换了,compareAndSet()返回true,否则,返回false。
使用Java5+提供的CAS特性而不是使用自己实现的的好处是Java5+中内置的CAS特性可以让你利用底层的你的程序所运行机器的CPU的CAS特性。这会使还有CAS的代码运行更快。
如果一个线程上了一个读锁,那么下一个线程是不是必须等待这个读锁释放才可以读?不是, 读与读可以并行, 只有遇到写才会等待, 如: 现在读着, 如果要写,则等待; 现在已经写着, 如果另外一个线程要写, 则等着; 现在写着, 如果要读, 则等着.
uname -r 显示正在使用的内核版本
cat,tail [file] 显示文件的信息
nano,vim [file] 编辑文件
ls, cd, rm(-rf),cp, pwd,mkdir 目录和文件管理
find / -name [file] 查找文件
useradd, userdel, passwd 管理用户
chmod [-cfvR] [666/777] [file] 管理文件权限
tar [-cvfz/-xvfz] [file] 压缩/解压文件
apt-get/yum install/update/upgrade/remove/clean 安装管理linux包
grep keyword [file] 在文件中查找关键词keyword
ifconfig 查看网卡信息
lsof -i:[pid] 查看pid的进程
ps ax | grep ruby 查看ruby相关进程
kill -9 [pid] 根据pid结束进程
单例对象的类必须保证只有一个实例存在, 永远返回同一个对象引用和一个获得该实例的方法getInstance(必须是静态方法), 两种实现方法, 饿汉式和懒汉式, 饿汉式单例实例在类装载时就构建, 线程安全, 但是资源效率不高, 即使不调用getInstance, 该单例实例也会被创建. 而懒汉式资源利用率高,不执行getInstance()就不会实例单例, 但是线程不安全, 多个线程同时访问的时候就可能创建多个实例, 一般使用synchonized同步和双重检测机制解决,但是仍然会有JVM编译器的指令重排导致的问题, 进一步使用修饰符volatile修饰实例对象. 另外还有静态内部类实现方式
应用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用:
应用场景举例:
参考1, 参考2
工厂模式抽象了对象创建的具体细节, 把创建对象过程封装了起来。简单工厂在构造方法中用if else语句来执行选择逻辑,通过传参来决定最后需要实例化的对象。但是,如果新增一个对象,那么我们还是要修改构造器,增加一个case,这样做并不好,特别是在多人协同实现这些对象的时候,这个构造器就要被同时修改,造成同步困难。
对简单工厂进行抽象就得到了工厂方法,每种产品由一种工厂来创建,一个工厂保存一个new,完全遵循“不改代码”的原则。而抽象工厂是工厂方法的复杂化,保存了多个new,可以把有一些有联系或者相近的产品,放到一个工厂去生产,没有必要单独再开一个工厂了。
必须满足以下四个必要条件,才会发生死锁:
避免出现死锁,只需要破坏四个中任意一个:
开放寻址法:若p=Hash(key)冲突的话,再用p作为key寻在下一个哈希地址p1=Hash(p), p2=Hash(p1), … , 直到找到一个不冲突的地址 pn p n 插入元素。
链地址法:对于冲突的元素,用一个单向链表储存,Hash表中的value储存的是链表的头节点。
比较:开放寻址为减少冲突,通常要求较小的装填因子 α<1 α < 1 ,不然随着数据增多,冲突会变多,因此尽量让数据稀疏;而链地址法可以让 α>1 α > 1 ,可以动态增加节点,它的删除操作比较简单,只需要删除相应的链表节点即可;但是开放寻址法比较复杂。
python如何打印一个类的所有成员变量:
for property, value in vars(theObject).iteritems():
python的dict底层实现 :
哈希表,最低能在O(1)时间内完成搜索, 采用开放寻址法解决hash冲突
python的dict和list占用内存谁大,大多少,为什么:
list的空间占用为 基本空间 36 + (对象引用 4 + 对象大小) * 元素个数, dict最小140, 因为需要缓存散列表, 所以占用内存多
python的list的查找元素时间复杂度: O(n)
python的list查找时间复杂读高, list占用内存大, 哪种数据结构能取得平衡:
查找二叉树, 插入数据和删除数据时间复杂度O(depth) 树的深度
mysql的索引为什么使得查找操作变快:
索引有Hash索引和B-Tree索引两种, 都能使查找变快
python反射:
反射就是通过字符串的形式,导入模块,执行函数。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动, 四个内置函数,getattr, setattr, getattr, delattr,如下profile为google.protobuf的对象,通过DESCRIPTOR.fields找到了所有的fields,每一个feild看作一个方法,因此可以通过getattr来调用:
for field in profile.DESCRIPTOR.fields:
print getattr(profile, field.name, 'not found')
线程池有两个元素,一个是queue, 一个是thread_list,队列里装的是(func, args)元组,func表示要调用的方法而args为方法参数;thread_list里装的是一些thread子类(个数上限max_nums),每个线程的run方法就是起一个死循环,循环内部执行:func,args=queue.get(block=True, time_out=5),然后调用方法func(args),这里queue是线程安全的,而且get方法是阻塞的,若等待时间超过time_out还未取到func和args,则抛出异常并break出循环,此时这个线程结束。
线程池在初始化的时候,会实例化固定数量的线程,每个线程都争着在queue对象中拿执行的对象和参数然后执行。现在假设我们有个task任务需要执行,那么可以直接把task和相关参数入队queue,然后某个线程便得到了task然后执行,但是,有可能现在所有的线程都已结束了(因为等待时间过长,“饿“死了),因此我们还需要在入队queue后立即调用一个refresh方法:移除当前thread_list中的已结束线程的线程,新增一些线程并保持活着的线程数为max_num
但是thread_list中的线程都是守护线程,thread.setDaemon(True),若main函数(主线程,且非守护线程)执行完毕,此时进程中已经没有非守护线程了,那么计算机会强制结束所有守护线程,因此我们一般在线程跑起来后,调用所有活着线程的join()方法来让main线程等待他们执行完毕后,再执行后面的代码。当然,若现在线程都是非守护线程的话,那么即使主线程结束了,子线程仍然会继续执行。
主线程能通过线程的事件Event来控制其他线程的执行,提供了三个方法,wait和clear,set,Event中有个全局flag(默认false),当flag为false时,程序执行到event.wait()时就会阻塞,此时若用event.set()方法能让flag为true,然后wait的地方继续执行;若用event.clear(),那么flag被重置为false,程序执行到wait后会阻塞。
根据以上,我们可以用以下方法手动阻塞线程:当程序运行到wait时,由于前面的clear方法阻塞,因此等待thread运行结束后,由thread调用set()方法通知程序继续往下运行
event.clear()
thread.start() # thread在结束时调用event.set()
event.wait()