Category Archives: JAVA

事务的ACID

事务的ACID是指什么?
答:
– 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
– 一致性(Consistent):事务结束后系统状态是一致的;
– 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
– 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

补充:关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一数据时,可能会存在5类问题,包括3类数据读取问题(脏读、不可重复读和幻读)和2类数据更新问题(第1类丢失更新和第2类丢失更新)。

脏读(Dirty Read):A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的数据就是脏数据。

时间 转账事务A 取款事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 取出500元余额修改为500元
T5 查询账户余额为500元(脏读)
T6 撤销事务余额恢复为1000元
T7 汇入100元把余额修改为600元
T8 提交事务

不可重复读(Unrepeatable Read):事务A重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务B修改过了。

时间 转账事务A 取款事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元修改余额为900元
T6 提交事务
T7 查询账户余额为900元(不可重复读)

幻读(Phantom Read):事务A重新执行一个查询,返回一系列符合查询条件的行,发现其中插入了被事务B提交的行。

时间 统计金额事务A 转账事务B
T1 开始事务
T2 开始事务
T3 统计总存款为10000元
T4 新增一个存款账户存入100元
T5 提交事务
T6 再次统计总存款为10100元(幻读)

第1类丢失更新:事务A撤销时,把已经提交的事务B的更新数据覆盖了。

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 汇入100元修改余额为1100元
T6 提交事务
T7 取出100元将余额修改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)

第2类丢失更新:事务A覆盖事务B已经提交的数据,造成事务B所做的操作丢失。

时间 转账事务A 取款事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元将余额修改为900元
T6 提交事务
T7 汇入100元将余额修改为1100元
T8 提交事务
T9 查询账户余额为1100元(丢失更新)

数据并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能就是致命的,数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁,具体的内容大家可以自行查阅资料进行了解。
直接使用锁是非常麻烦的,为此数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对用户来说都是透明的(就是说你不用理解,事实上我确实也不知道)。ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,如下表所示:

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
READ UNCOMMITED 允许 允许 允许 不允许 允许
READ COMMITTED 不允许 允许 允许 不允许 允许
REPEATABLE READ 不允许 不允许 允许 不允许 不允许
SERIALIZABLE 不允许 不允许 不允许 不允许 不允许

需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没有万能的原则。

JDBC中如何进行事务处理?
答:Connection提供了事务处理的方法,通过调用setAutoCommit(false)可以设置手动提交事务;当事务完成后用commit()显式提交事务;如果在事务处理过程中发生异常则通过rollback()进行事务回滚。除此之外,从JDBC 3.0中还引入了Savepoint(保存点)的概念,允许通过代码设置保存点并让事务回滚到指定的保存点。
这里写图片描述

refer:Java面试题全集

关于Java并发编程的总结和思考

为什么需要并发

并发其实是一种解耦合的策略,它帮助我们把做什么(目标)和什么时候做(时机)分开。这样做可以明显改进应用程序的吞吐量(获得更多的CPU调度时间)和结构(程序有多个部分在协同工作)。做过Java Web开发的人都知道,Java Web中的Servlet程序在Servlet容器的支持下采用单实例多线程的工作模式,Servlet容器为你处理了并发问题。

误解和正解

最常见的对并发编程的误解有以下这些:
-并发总能改进性能(并发在CPU有很多空闲时间时能明显改进程序的性能,但当线程数量较多的时候,线程间频繁的调度切换反而会让系统的性能下降)
-编写并发程序无需修改原有的设计(目的与时机的解耦往往会对系统结构产生巨大的影响)
-在使用Web或EJB容器时不用关注并发问题(只有了解了容器在做什么,才能更好的使用容器)
下面的这些说法才是对并发客观的认识:
编写并发程序会在代码上增加额外的开销
-正确的并发是非常复杂的,即使对于很简单的问题
-并发中的缺陷因为不易重现也不容易被发现
-并发往往需要对设计策略从根本上进行修改

并发编程的原则和技巧

单一职责原则

分离并发相关代码和其他代码(并发相关代码有自己的开发、修改和调优生命周期)。

限制数据作用域

两个线程修改共享对象的同一字段时可能会相互干扰,导致不可预期的行为,解决方案之一是构造临界区,但是必须限制临界区的数量。

使用数据副本

数据副本是避免共享数据的好方法,复制出来的对象只是以只读的方式对待。Java 5的java.util.concurrent包中增加一个名为CopyOnWriteArrayList的类,它是List接口的子类型,所以你可以认为它是ArrayList的线程安全的版本,它使用了写时复制的方式创建数据副本进行操作来避免对共享数据并发访问而引发的问题。

线程应尽可能独立

让线程存在于自己的世界中,不与其他线程共享数据。有过Java Web开发经验的人都知道,Servlet就是以单实例多线程的方式工作,和每个请求相关的数据都是通过Servlet子类的service方法(或者是doGet或doPost方法)的参数传入的。只要Servlet中的代码只使用局部变量,Servlet就不会导致同步问题。springMVC的控制器也是这么做的,从请求中获得的对象都是以方法的参数传入而不是作为类的成员,很明显Struts 2的做法就正好相反,因此Struts 2中作为控制器的Action类都是每个请求对应一个实例。

Java 5以前的并发编程

Java的线程模型建立在抢占式线程调度的基础上,也就是说:

  • 所有线程可以很容易的共享同一进程中的对象。
  • 能够引用这些对象的任何线程都可以修改这些对象。
  • 为了保护数据,对象可以被锁住。

Java基于线程和锁的并发过于底层,而且使用锁很多时候都是很万恶的,因为它相当于让所有的并发都变成了排队等待。
在Java 5以前,可以用synchronized关键字来实现锁的功能,它可以用在代码块和方法上,表示在执行整个代码块或方法之前线程必须取得合适的锁。对于类的非静态方法(成员方法)而言,这意味这要取得对象实例的锁,对于类的静态方法(类方法)而言,要取得类的Class对象的锁,对于同步代码块,程序员可以指定要取得的是那个对象的锁。
不管是同步代码块还是同步方法,每次只有一个线程可以进入,如果其他线程试图进入(不管是同一同步块还是不同的同步块),JVM会将它们挂起(放入到等锁池中)。这种结构在并发理论中称为临界区(critical section)。这里我们可以对Java中用synchronized实现同步和锁的功能做一个总结:

  • 只能锁定对象,不能锁定基本数据类型
  • 被锁定的对象数组中的单个对象不会被锁定
  • 同步方法可以视为包含整个方法的synchronized(this) { … }代码块
  • 静态同步方法会锁定它的Class对象
  • 内部类的同步是独立于外部类的
  • synchronized修饰符并不是方法签名的组成部分,所以不能出现在接口的方法声明中
  • 非同步的方法不关心锁的状态,它们在同步方法运行时仍然可以得以运行
  • synchronized实现的锁是可重入的锁。

在JVM内部,为了提高效率,同时运行的每个线程都会有它正在处理的数据的缓存副本,当我们使用synchronzied进行同步的时候,真正被同步的是在不同线程中表示被锁定对象的内存块(副本数据会保持和主内存的同步,现在知道为什么要用同步这个词汇了吧),简单的说就是在同步块或同步方法执行完后,对被锁定的对象做的任何修改要在释放锁之前写回到主内存中;在进入同步块得到锁之后,被锁定对象的数据是从主内存中读出来的,持有锁的线程的数据副本一定和主内存中的数据视图是同步的 。
在Java最初的版本中,就有一个叫volatile的关键字,它是一种简单的同步的处理机制,因为被volatile修饰的变量遵循以下规则:

  • 变量的值在使用之前总会从主内存中再读取出来。
  • 对变量值的修改总会在完成之后写回到主内存中。

使用volatile关键字可以在多线程环境下预防编译器不正确的优化假设(编译器可能会将在一个线程中值不会发生改变的变量优化成常量),但只有修改时不依赖当前状态(读取时的值)的变量才应该声明为volatile变量。
不变模式也是并发编程时可以考虑的一种设计。让对象的状态是不变的,如果希望修改对象的状态,就会创建对象的副本并将改变写入副本而不改变原来的对象,这样就不会出现状态不一致的情况,因此不变对象是线程安全的。Java中我们使用频率极高的String类就采用了这样的设计。如果对不变模式不熟悉,可以阅读阎宏博士的《Java与模式》一书的第34章。说到这里你可能也体会到final关键字的重要意义了。

Java 5的并发编程

不管今后的Java向着何种方向发展或者灭亡,Java 5绝对是Java发展史中一个极其重要的版本,这个版本提供的各种语言特性我们不在这里讨论(有兴趣的可以阅读我的另一篇文章《Java的第20年:从Java版本演进看编程技术的发展》),但是我们必须要感谢Doug Lea在Java 5中提供了他里程碑式的杰作java.util.concurrent包,它的出现让Java的并发编程有了更多的选择和更好的工作方式。Doug Lea的杰作主要包括以下内容:

  • 更好的线程安全的容器
  • 线程池和相关的工具类
  • 可选的非阻塞解决方案
  • 显示的锁和信号量机制

下面我们对这些东西进行一一解读。

原子类

Java 5中的java.util.concurrent包下面有一个atomic子包,其中有几个以Atomic打头的类,例如AtomicInteger和AtomicLong。它们利用了现代处理器的特性,可以用非阻塞的方式完成原子操作,代码如下所示:

/**
 ID序列生成器
*/
public class IdGenerator {
    private final AtomicLong sequenceNumber = new AtomicLong(0);

    public long next() {
        return sequenceNumber.getAndIncrement(); 
    }
}

显示锁

基于synchronized关键字的锁机制有以下问题:

  • 锁只有一种类型,而且对所有同步操作都是一样的作用
  • 锁只能在代码块或方法开始的地方获得,在结束的地方释放
  • 线程要么得到锁,要么阻塞,没有其他的可能性

Java 5对锁机制进行了 重构,提供了显示的锁,这样可以在以下几个方面提升锁机制:

  • 可以添加不同类型的锁,例如读取锁和写入锁
  • 可以在一个方法中加锁,在另一个方法中解锁
  • 可以使用tryLock方式尝试获得锁,如果得不到锁可以等待、回退或者干点别的事情,当然也可以在超时之后放弃操作

显示的锁都实现了java.util.concurrent.Lock接口,主要有两个实现类:

  • ReentrantLock – 比synchronized稍微灵活一些的重入锁
  • ReentrantReadWriteLock – 在读操作很多写操作很少时性能更好的一种重入锁

对于如何使用显示锁,可以参考我的Java面试系列文章 《Java面试题集51-70》中第60题的代码。只有一点需要提醒,解锁的方法unlock的调用最好能够在finally块中,因为这里是释放外部资源最好的地方,当然也是释放锁的最佳位置,因为不管正常异常可能都要释放掉锁来给其他线程以运行的机会。

CountDownLatch

CountDownLatch是一种简单的同步模式,它让一个线程可以等待一个或多个线程完成它们的工作从而避免对临界资源并发访问所引发的各种问题。下面借用别人的一段代码(我对它做了一些重构)来演示CountDownLatch是如何工作的。

import java.util.concurrent.CountDownLatch;

/**
 * 工人类
 * @author 骆昊
 *
 */
class Worker {
    private String name;        // 名字
    private long workDuration;  // 工作持续时间

    /**
     * 构造器
     */
    public Worker(String name, long workDuration) {
        this.name = name;
        this.workDuration = workDuration;
    }

    /**
     * 完成工作
     */
    public void doWork() {
        System.out.println(name + " begins to work...");
        try {
            Thread.sleep(workDuration); // 用休眠模拟工作执行的时间
        } catch(InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println(name + " has finished the job...");
    }
}

/**
 * 测试线程
 * @author 骆昊
 *
 */
class WorkerTestThread implements Runnable {
    private Worker worker;
    private CountDownLatch cdLatch;

    public WorkerTestThread(Worker worker, CountDownLatch cdLatch) {
        this.worker = worker;
        this.cdLatch = cdLatch;
    }

    @Override
    public void run() {
        worker.doWork();        // 让工人开始工作
        cdLatch.countDown();    // 工作完成后倒计时次数减1
    }
}

class CountDownLatchTest {

    private static final int MAX_WORK_DURATION = 5000;  // 最大工作时间
    private static final int MIN_WORK_DURATION = 1000;  // 最小工作时间

    // 产生随机的工作时间
    private static long getRandomWorkDuration(long min, long max) {
        return (long) (Math.random() * (max - min) + min);
    }

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(2);   // 创建倒计时闩并指定倒计时次数为2
        Worker w1 = new Worker("骆昊", getRandomWorkDuration(MIN_WORK_DURATION, MAX_WORK_DURATION));
        Worker w2 = new Worker("王大锤", getRandomWorkDuration(MIN_WORK_DURATION, MAX_WORK_DURATION));

        new Thread(new WorkerTestThread(w1, latch)).start();
        new Thread(new WorkerTestThread(w2, latch)).start();

        try {
            latch.await();  // 等待倒计时闩减到0
            System.out.println("All jobs have been finished!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ConcurrentHashMap

ConcurrentHashMap是HashMap在并发环境下的版本,大家可能要问,既然已经可以通过Collections.synchronizedMap获得线程安全的映射型容器,为什么还需要ConcurrentHashMap呢?因为通过Collections工具类获得的线程安全的HashMap会在读写数据时对整个容器对象上锁,这样其他使用该容器的线程无论如何也无法再获得该对象的锁,也就意味着要一直等待前一个获得锁的线程离开同步代码块之后才有机会执行。实际上,HashMap是通过哈希函数来确定存放键值对的桶(桶是为了解决哈希冲突而引入的),修改HashMap时并不需要将整个容器锁住,只需要锁住即将修改的“桶”就可以了。HashMap的数据结构如下图所示。
这里写图片描述

此外,ConcurrentHashMap还提供了原子操作的方法,如下所示:

  • putIfAbsent:如果还没有对应的键值对映射,就将其添加到HashMap中。
  • remove:如果键存在而且值与当前状态相等(equals比较结果为true),则用原子方式移除该键值对映射
  • replace:替换掉映射中元素的原子操作

CopyOnWriteArrayList

CopyOnWriteArrayList是ArrayList在并发环境下的替代品。CopyOnWriteArrayList通过增加写时复制语义来避免并发访问引起的问题,也就是说任何修改操作都会在底层创建一个列表的副本,也就意味着之前已有的迭代器不会碰到意料之外的修改。这种方式对于不要严格读写同步的场景非常有用,因为它提供了更好的性能。记住,要尽量减少锁的使用,因为那势必带来性能的下降(对数据库中数据的并发访问不也是如此吗?如果可以的话就应该放弃悲观锁而使用乐观锁),CopyOnWriteArrayList很明显也是通过牺牲空间获得了时间(在计算机的世界里,时间和空间通常是不可调和的矛盾,可以牺牲空间来提升效率获得时间,当然也可以通过牺牲时间来减少对空间的使用)。
这里写图片描述

可以通过下面两段代码的运行状况来验证一下CopyOnWriteArrayList是不是线程安全的容器。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class AddThread implements Runnable {
    private List<Double> list;

    public AddThread(List<Double> list) {
        this.list = list;
    }

    @Override
    public void run() {
        for(int i = 0; i < 10000; ++i) {
            list.add(Math.random());
        }
    }
}

public class Test05 {
    private static final int THREAD_POOL_SIZE = 2;

    public static void main(String[] args) {
        List<Double> list = new ArrayList<>();
        ExecutorService es = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        es.execute(new AddThread(list));
        es.execute(new AddThread(list));
        es.shutdown();
    }
}

上面的代码会在运行时产生ArrayIndexOutOfBoundsException,试一试将上面代码25行的ArrayList换成CopyOnWriteArrayList再重新运行。

List<Double> list = new CopyOnWriteArrayList<>();

Queue

队列是一个无处不在的美妙概念,它提供了一种简单又可靠的方式将资源分发给处理单元(也可以说是将工作单元分配给待处理的资源,这取决于你看待问题的方式)。实现中的并发编程模型很多都依赖队列来实现,因为它可以在线程之间传递工作单元。
Java 5中的BlockingQueue就是一个在并发环境下非常好用的工具,在调用put方法向队列中插入元素时,如果队列已满,它会让插入元素的线程等待队列腾出空间;在调用take方法从队列中取元素时,如果队列为空,取出元素的线程就会阻塞。
这里写图片描述
可以用BlockingQueue来实现生产者-消费者并发模型(下一节中有介绍),当然在Java 5以前也可以通过wait和notify来实现线程调度,比较一下两种代码就知道基于已有的并发工具类来重构并发代码到底好在哪里了。

  • 基于wait和notify的实现
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 公共常量
 * @author 骆昊
 *
 */
class Constants {
    public static final int MAX_BUFFER_SIZE = 10;
    public static final int NUM_OF_PRODUCER = 2;
    public static final int NUM_OF_CONSUMER = 3;
}

/**
 * 工作任务
 * @author 骆昊
 *
 */
class Task {
    private String id;  // 任务的编号

    public Task() {
        id = UUID.randomUUID().toString();
    }

    @Override
    public String toString() {
        return "Task[" + id + "]";
    }
}

/**
 * 消费者
 * @author 骆昊
 *
 */
class Consumer implements Runnable {
    private List<Task> buffer;

    public Consumer(List<Task> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while(true) {
            synchronized(buffer) {
                while(buffer.isEmpty()) {
                    try {
                        buffer.wait();
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Task task = buffer.remove(0);
                buffer.notifyAll();
                System.out.println("Consumer[" + Thread.currentThread().getName() + "] got " + task);
            }
        }
    }
}

/**
 * 生产者
 * @author 骆昊
 *
 */
class Producer implements Runnable {
    private List<Task> buffer;

    public Producer(List<Task> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while(true) {
            synchronized (buffer) {
                while(buffer.size() >= Constants.MAX_BUFFER_SIZE) {
                    try {
                        buffer.wait();
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Task task = new Task();
                buffer.add(task);
                buffer.notifyAll();
                System.out.println("Producer[" + Thread.currentThread().getName() + "] put " + task);
            }
        }
    }

}

public class Test06 {

    public static void main(String[] args) {
        List<Task> buffer = new ArrayList<>(Constants.MAX_BUFFER_SIZE);
        ExecutorService es = Executors.newFixedThreadPool(Constants.NUM_OF_CONSUMER + Constants.NUM_OF_PRODUCER);
        for(int i = 1; i <= Constants.NUM_OF_PRODUCER; ++i) {
            es.execute(new Producer(buffer));
        }
        for(int i = 1; i <= Constants.NUM_OF_CONSUMER; ++i) {
            es.execute(new Consumer(buffer));
        }
    }
}
  • 基于BlockingQueue的实现
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 公共常量
 * @author 骆昊
 *
 */
class Constants {
    public static final int MAX_BUFFER_SIZE = 10;
    public static final int NUM_OF_PRODUCER = 2;
    public static final int NUM_OF_CONSUMER = 3;
}

/**
 * 工作任务
 * @author 骆昊
 *
 */
class Task {
    private String id;  // 任务的编号

    public Task() {
        id = UUID.randomUUID().toString();
    }

    @Override
    public String toString() {
        return "Task[" + id + "]";
    }
}

/**
 * 消费者
 * @author 骆昊
 *
 */
class Consumer implements Runnable {
    private BlockingQueue<Task> buffer;

    public Consumer(BlockingQueue<Task> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Task task = buffer.take();
                System.out.println("Consumer[" + Thread.currentThread().getName() + "] got " + task);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 生产者
 * @author 骆昊
 *
 */
class Producer implements Runnable {
    private BlockingQueue<Task> buffer;

    public Producer(BlockingQueue<Task> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Task task = new Task();
                buffer.put(task);
                System.out.println("Producer[" + Thread.currentThread().getName() + "] put " + task);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

}

public class Test07 {

    public static void main(String[] args) {
        BlockingQueue<Task> buffer = new LinkedBlockingQueue<>(Constants.MAX_BUFFER_SIZE);
        ExecutorService es = Executors.newFixedThreadPool(Constants.NUM_OF_CONSUMER + Constants.NUM_OF_PRODUCER);
        for(int i = 1; i <= Constants.NUM_OF_PRODUCER; ++i) {
            es.execute(new Producer(buffer));
        }
        for(int i = 1; i <= Constants.NUM_OF_CONSUMER; ++i) {
            es.execute(new Consumer(buffer));
        }
    }
}

使用BlockingQueue后代码优雅了很多。

并发模型

在继续下面的探讨之前,我们还是重温一下几个概念:

概念 解释
临界资源 并发环境中有着固定数量的资源
互斥 对资源的访问是排他式的
饥饿 一个或一组线程长时间或永远无法取得进展
死锁 两个或多个线程相互等待对方结束
活锁 想要执行的线程总是发现其他的线程正在执行以至于长时间或永远无法执行

重温了这几个概念后,我们可以探讨一下下面的几种并发模型。

生产者-消费者

一个或多个生产者创建某些工作并将其置于缓冲区或队列中,一个或多个消费者会从队列中获得这些工作并完成之。这里的缓冲区或队列是临界资源。当缓冲区或队列放满的时候,生产这会被阻塞;而缓冲区或队列为空的时候,消费者会被阻塞。生产者和消费者的调度是通过二者相互交换信号完成的。

读者-写者

当存在一个主要为读者提供信息的共享资源,它偶尔会被写者更新,但是需要考虑系统的吞吐量,又要防止饥饿和陈旧资源得不到更新的问题。在这种并发模型中,如何平衡读者和写者是最困难的,当然这个问题至今还是一个被热议的问题,恐怕必须根据具体的场景来提供合适的解决方案而没有那种放之四海而皆准的方法(不像我在国内的科研文献中看到的那样)。

哲学家进餐

1965年,荷兰计算机科学家图灵奖得主Edsger Wybe Dijkstra提出并解决了一个他称之为哲学家进餐的同步问题。这个问题可以简单地描述如下:五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子如下图所示。哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。
把上面问题中的哲学家换成线程,把叉子换成竞争的临界资源,上面的问题就是线程竞争资源的问题。如果没有经过精心的设计,系统就会出现死锁、活锁、吞吐量下降等问题。
这里写图片描述
下面是用信号量原语来解决哲学家进餐问题的代码,使用了Java 5并发工具包中的Semaphore类(代码不够漂亮但是已经足以说明问题了)。

//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 存放线程共享信号量的上下问
 * @author 骆昊
 *
 */
class AppContext {
    public static final int NUM_OF_FORKS = 5;   // 叉子数量(资源)
    public static final int NUM_OF_PHILO = 5;   // 哲学家数量(线程)

    public static Semaphore[] forks;    // 叉子的信号量
    public static Semaphore counter;    // 哲学家的信号量

    static {
        forks = new Semaphore[NUM_OF_FORKS];

        for (int i = 0, len = forks.length; i < len; ++i) {
            forks[i] = new Semaphore(1);    // 每个叉子的信号量为1
        }

        counter = new Semaphore(NUM_OF_PHILO - 1);  // 如果有N个哲学家,最多只允许N-1人同时取叉子
    }

    /**
     * 取得叉子
     * @param index 第几个哲学家
     * @param leftFirst 是否先取得左边的叉子
     * @throws InterruptedException
     */
    public static void putOnFork(int index, boolean leftFirst) throws InterruptedException {
        if(leftFirst) {
            forks[index].acquire();
            forks[(index + 1) % NUM_OF_PHILO].acquire();
        }
        else {
            forks[(index + 1) % NUM_OF_PHILO].acquire();
            forks[index].acquire();
        }
    }

    /**
     * 放回叉子
     * @param index 第几个哲学家
     * @param leftFirst 是否先放回左边的叉子
     * @throws InterruptedException
     */
    public static void putDownFork(int index, boolean leftFirst) throws InterruptedException {
        if(leftFirst) {
            forks[index].release();
            forks[(index + 1) % NUM_OF_PHILO].release();
        }
        else {
            forks[(index + 1) % NUM_OF_PHILO].release();
            forks[index].release();
        }
    }
}

/**
 * 哲学家
 * @author 骆昊
 *
 */
class Philosopher implements Runnable {
    private int index;      // 编号
    private String name;    // 名字

    public Philosopher(int index, String name) {
        this.index = index;
        this.name = name;
    }

    @Override
    public void run() {
        while(true) {
            try {
                AppContext.counter.acquire();
                boolean leftFirst = index % 2 == 0;
                AppContext.putOnFork(index, leftFirst);
                System.out.println(name + "正在吃意大利面(通心粉)...");   // 取到两个叉子就可以进食
                AppContext.putDownFork(index, leftFirst);
                AppContext.counter.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Test04 {

    public static void main(String[] args) {
        String[] names = { "骆昊", "王大锤", "张三丰", "杨过", "李莫愁" };   // 5位哲学家的名字
//      ExecutorService es = Executors.newFixedThreadPool(AppContext.NUM_OF_PHILO); // 创建固定大小的线程池
//      for(int i = 0, len = names.length; i < len; ++i) {
//          es.execute(new Philosopher(i, names[i]));   // 启动线程
//      }
//      es.shutdown();
        for(int i = 0, len = names.length; i < len; ++i) {
            new Thread(new Philosopher(i, names[i])).start();
        }
    }

}

现实中的并发问题基本上都是这三种模型或者是这三种模型的变体。

测试并发代码

对并发代码的测试也是非常棘手的事情,棘手到无需说明大家也很清楚的程度,所以这里我们只是探讨一下如何解决这个棘手的问题。我们建议大家编写一些能够发现问题的测试并经常性的在不同的配置和不同的负载下运行这些测试。不要忽略掉任何一次失败的测试,线程代码中的缺陷可能在上万次测试中仅仅出现一次。具体来说有这么几个注意事项:

  • 不要将系统的失效归结于偶发事件,就像拉不出屎的时候不能怪地球没有引力。
  • 先让非并发代码工作起来,不要试图同时找到并发和非并发代码中的缺陷。
  • 编写可以在不同配置环境下运行的线程代码。
  • 编写容易调整的线程代码,这样可以调整线程使性能达到最优。
  • 让线程的数量多于CPU或CPU核心的数量,这样CPU调度切换过程中潜在的问题才会暴露出来。
  • 让并发代码在不同的平台上运行。
  • 通过自动化或者硬编码的方式向并发代码中加入一些辅助测试的代码。

Java 7的并发编程

Java 7中引入了TransferQueue,它比BlockingQueue多了一个叫transfer的方法,如果接收线程处于等待状态,该操作可以马上将任务交给它,否则就会阻塞直至取走该任务的线程出现。可以用TransferQueue代替BlockingQueue,因为它可以获得更好的性能。
刚才忘记了一件事情,Java 5中还引入了Callable接口、Future接口和FutureTask接口,通过他们也可以构建并发应用程序,代码如下所示。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test07 {
    private static final int POOL_SIZE = 10;

    static class CalcThread implements Callable<Double> {
        private List<Double> dataList = new ArrayList<>();

        public CalcThread() {
            for(int i = 0; i < 10000; ++i) {
                dataList.add(Math.random());
            }
        }

        @Override
        public Double call() throws Exception {
            double total = 0;
            for(Double d : dataList) {
                total += d;
            }
            return total / dataList.size();
        }

    }

    public static void main(String[] args) {
        List<Future<Double>> fList = new ArrayList<>();
        ExecutorService es = Executors.newFixedThreadPool(POOL_SIZE);
        for(int i = 0; i < POOL_SIZE; ++i) {
            fList.add(es.submit(new CalcThread()));
        }

        for(Future<Double> f : fList) {
            try {
                System.out.println(f.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        es.shutdown();
    }
}

Callable接口也是一个单方法接口,显然这是一个回调方法,类似于函数式编程中的回调函数,在Java 8 以前,Java中还不能使用Lambda表达式来简化这种函数式编程。和Runnable接口不同的是Callable接口的回调方法call方法会返回一个对象,这个对象可以用将来时的方式在线程执行结束的时候获得信息。上面代码中的call方法就是将计算出的10000个0到1之间的随机小数的平均值返回,我们通过一个Future接口的对象得到了这个返回值。目前最新的Java版本中,Callable接口和Runnable接口都被打上了@FunctionalInterface的注解,也就是说它可以用函数式编程的方式(Lambda表达式)创建接口对象。
下面是Future接口的主要方法:

  • get():获取结果。如果结果还没有准备好,get方法会阻塞直到取得结果;当然也可以通过参数设置阻塞超时时间。
  • cancel():在运算结束前取消。
  • isDone():可以用来判断运算是否结束。

Java 7中还提供了分支/合并(fork/join)框架,它可以实现线程池中任务的自动调度,并且这种调度对用户来说是透明的。为了达到这种效果,必须按照用户指定的方式对任务进行分解,然后再将分解出的小型任务的执行结果合并成原来任务的执行结果。这显然是运用了分治法(divide-and-conquer)的思想。下面的代码使用了分支/合并框架来计算1到10000的和,当然对于如此简单的任务根本不需要分支/合并框架,因为分支和合并本身也会带来一定的开销,但是这里我们只是探索一下在代码中如何使用分支/合并框架,让我们的代码能够充分利用现代多核CPU的强大运算能力。

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

class Calculator extends RecursiveTask<Integer> {
    private static final long serialVersionUID = 7333472779649130114L;

    private static final int THRESHOLD = 10;
    private int start;
    private int end;

    public Calculator(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Integer compute() {
        int sum = 0;
        if ((end - start) < THRESHOLD) {    // 当问题分解到可求解程度时直接计算结果
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            int middle = (start + end) >>> 1;
            // 将任务一分为二
            Calculator left = new Calculator(start, middle);
            Calculator right = new Calculator(middle + 1, end);
            left.fork();
            right.fork();
            // 注意:由于此处是递归式的任务分解,也就意味着接下来会二分为四,四分为八...

            sum = left.join() + right.join();   // 合并两个子任务的结果
        }
        return sum;
    }

}

public class Test08 {

    public static void main(String[] args) throws Exception {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        Future<Integer> result = forkJoinPool.submit(new Calculator(1, 10000));
        System.out.println(result.get());
    }
}

伴随着Java 7的到来,Java中默认的数组排序算法已经不再是经典的快速排序(双枢轴快速排序)了,新的排序算法叫TimSort,它是归并排序和插入排序的混合体,TimSort可以通过分支合并框架充分利用现代处理器的多核特性,从而获得更好的性能(更短的排序时间)。

参考文献

  1. Benjamin J. Evans, etc, The Well-Grounded Java Developer. Jul 21, 2012
  2. Robert Martin, Clean Code. Aug 11, 2008.
  3. Doug Lea, Concurrent Programming in Java: Design Principles and Patterns. 1999

from:http://www.importnew.com/22286.html

Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例

 

“看看星空,会觉得自己很渺小,可能我们在宇宙中从来就是一个偶然。所以,无论什么事情,仔细想一想,都没有什么大不了的。这能帮助自己在遇到挫折时稳定心态,想得更开。”  – 《腾讯传》
本文提纲
一、为啥整合 Dubbo 实现 SOA
二、运行 springboot-dubbo-server 和 springboot-dubbo-client 工程
三、springboot-dubbo-server 和 springboot-dubbo-client 工程配置详解
 

一、为啥整合 Dubbo 实现 SOA

Dubbo 不单单只是高性能的 RPC 调用框架,更是 SOA 服务治理的一种方案。
核心
1. 远程通信,向本地调用一样调用远程方法。
2. 集群容错
3. 服务自动发现和注册,可平滑添加或者删除服务提供者。
我们常常使用 Springboot 暴露 HTTP 服务,并走 JSON 模式。但慢慢量大了,一种 SOA 的治理方案。这样可以暴露出 Dubbo 服务接口,提供给 Dubbo 消费者进行 RPC 调用。下面我们详解下如何集成 Dubbo。

二、运行 springboot-dubbo-server 和 springboot-dubbo-client 工程

运行环境:JDK 7 或 8,Maven 3.0+
技术栈:SpringBoot 1.5+、Dubbo 2.5+、ZooKeeper 3.3+

 

1.ZooKeeper 服务注册中心
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
下载 ZooKeeper ,地址 http://www.apache.org/dyn/closer.cgi/zookeeper
解压 ZooKeeper
tar zxvf zookeeper-3.4.8.tar.gz
在 conf 目录新建 zoo.cfg ,照着该目录的 zoo_sample.cfg 配置如下。
cd zookeeper-3.3.6/conf
vim zoo.cfg
zoo.cfg 代码如下(自己指定 log 文件目录):
tickTime=2000
dataDir=/javaee/zookeeper/data 
dataLogDir=/javaee/zookeeper/log
clientPort=2181
在 bin 目录下,启动 ZooKeeper:
cd zookeeper-3.3.6/bin
./zkServer.sh start
2. git clone 下载工程 springboot-learning-example
git clone git@github.com:JeffLi1993/springboot-learning-example.git

然后,Maven 编译安装这个工程:

cd springboot-learning-example
mvn clean install
3.运行 springboot-dubbo-server Dubbo 服务提供者工程
右键运行 springboot-dubbo-server 工程 ServerApplication 应用启动类的 main 函数。Console 中出现如下表示项目启动成功:
这里表示 Dubbo 服务已经启动成功,并注册到 ZK (ZooKeeper)中。
4.运行 springboot-dubbo-client Dubbo 服务消费者工程

右键运行 springboot-dubbo-client 工程 ClientApplication 应用启动类的 main 函数。Console 中出现如下:

...
2017-03-01 16:31:38.473  INFO 9896 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-03-01 16:31:38.538  INFO 9896 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8081 (http)
2017-03-01 16:31:38.547  INFO 9896 --- [           main] org.spring.springboot.ClientApplication  : Started ClientApplication in 6.055 seconds (JVM running for 7.026)
City{id=1, provinceId=2, cityName='温岭', description='是我的故乡'}
最后打印的城市信息,就是通过 Dubbo 服务接口调用获取的。顺利运行成功,下面详解下各个代码及配置。

三、springboot-dubbo-server 和 springboot-dubbo-client 工程配置详解

1.详解 springboot-dubbo-server Dubbo 服务提供者工程
springboot-dubbo-server 工程目录结构
├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── ServerApplication.java
        │               ├── domain
        │               │   └── City.java
        │               └── dubbo
        │                   ├── CityDubboService.java
        │                   └── impl
        │                       └── CityDubboServiceImpl.java
        └── resources
            └── application.properties
a.pom.xml 配置

pom.xml 中依赖了 spring-boot-starter-dubbo 工程,该项目地址是 https://github.com/teaey/spring-boot-starter-dubbo。pom.xml 配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>springboot</groupId>
<artifactId>springboot-dubbo-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-dubbo 服务端:: 整合 Dubbo/ZooKeeper 详解 SOA 案例</name>

<!-- Spring Boot 启动父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>

<properties>
<dubbo-spring-boot>1.0.0</dubbo-spring-boot>
</properties>

<dependencies>

<!-- Spring Boot Dubbo 依赖 -->
<dependency>
<groupId>io.dubbo.springboot</groupId>
<artifactId>spring-boot-starter-dubbo</artifactId>
<version>${dubbo-spring-boot}</version>
</dependency>

<!-- Spring Boot Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Boot Test 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>

b.application.properties 配置

## Dubbo 服务提供者配置
spring.dubbo.application.name=provider
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880
spring.dubbo.scan=org.spring.springboot.dubbo
这里 ZK 配置的地址和端口,就是上面本机搭建的 ZK 。如果有自己的 ZK 可以修改下面的配置。配置解释如下:
spring.dubbo.application.name 应用名称
spring.dubbo.registry.address 注册中心地址
spring.dubbo.protocol.name 协议名称
spring.dubbo.protocol.port 协议端口
spring.dubbo.scan dubbo 服务类包目录
c.CityDubboServiceImpl.java 城市业务 Dubbo 服务层实现层类
// 注册为 Dubbo 服务
@Service(version = "1.0.0")
public class CityDubboServiceImpl implements CityDubboService {

    public City findCityByName(String cityName) {
        return new City(1L,2L,"温岭","是我的故乡");
    }
}
@Service 注解标识为 Dubbo 服务,并通过 version 指定了版本号。
d.City.java 城市实体类
实体类通过 Dubbo 服务之间 RPC 调用,则需要实现序列化接口。最好指定下 serialVersionUID 值。
2.详解 springboot-dubbo-client Dubbo 服务消费者工程
springboot-dubbo-client 工程目录结构
├── pom.xml
└── src
    └── main
        ├── java
        │   └── org
        │       └── spring
        │           └── springboot
        │               ├── ClientApplication.java
        │               ├── domain
        │               │   └── City.java
        │               └── dubbo
        │                   ├── CityDubboConsumerService.java
        │                   └── CityDubboService.java
        └── resources
            └── application.properties
pom.xml 、 CityDubboService.java、City.java 没有改动。Dubbo 消费者通过引入接口实现 Dubbo 接口的调用。
a.application.properties 配置
## 避免和 server 工程端口冲突
server.port=8081

## Dubbo 服务消费者配置
spring.dubbo.application.name=consumer
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181
spring.dubbo.scan=org.spring.springboot.dubbo
因为 springboot-dubbo-server 工程启动占用了 8080 端口,所以这边设置端口为 8081。
b.CityDubboConsumerService.java 城市 Dubbo 服务消费者
@Component
public class CityDubboConsumerService {

    @Reference(version = "1.0.0")
    CityDubboService cityDubboService;

    public void printCity() {
        String cityName="温岭";
        City city = cityDubboService.findCityByName(cityName);
        System.out.println(city.toString());
    }
}
@Reference(version = “1.0.0”) 通过该注解,订阅该接口版本为 1.0.0 的 Dubbo 服务。
这里将 CityDubboConsumerService 注入 Spring 容器,是为了更方便的获取该 Bean,然后验证这个 Dubbo 调用是否成功。
c.ClientApplication.java 客户端启动类
@SpringBootApplication
public class ClientApplication {

    public static void main(String[] args) {
        // 程序启动入口
        // 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
        ConfigurableApplicationContext run = SpringApplication.run(ClientApplication.class, args);
        CityDubboConsumerService cityService = run.getBean(CityDubboConsumerService.class);
        cityService.printCity();
    }
}
解释下这段逻辑,就是启动后从 Bean 容器中获取城市 Dubbo 服务消费者 Bean。然后调用该 Bean 方法去验证 Dubbo 调用是否成功。

四、小结

还有涉及到服务的监控,治理。这本质上和 SpringBoot 无关,所以这边不做一一介绍。感谢阿里 teaey 提供的 starter-dubbo 项目。

垃圾回收原来是这么回事

最近想复习一下JVM的知识。然后发现网上不少文章在写JVM的垃圾回收算法时,都比较偏向于具体实现,而少有站在更高角度来看垃圾回收算法的文章。而我本人想对垃圾回收算法有个全景的认识,所以,就找到了这本《垃圾回收的算法与实现》(以下简称《垃圾回收》)。本篇博客就是尝试对“全景”的总结。

以下为方便讨论,垃圾回收缩写成GC。

  为什么要有GC

我时而听到C++程序员说我们是被GC惯坏了的一代。的确是这样的,我本人在学习GC算法时,大脑里第一问题就是为什么需要GC这样的东西。说明我已经认为GC是理所当然了。

总的一句话:没有GC的世界,我们需要手动进行内存管理,而手动内存管理是纯技术活,又容易出错。

既然我们写的大多程序都是为了解决现实业务问题,那么,我们为什么不把这种纯技术活自动化呢?但是自动化,也是有代价的。 这是我的个人理解,不代表John McCarthy本人的理解

  “垃圾”的定义

首先,我们要给个“垃圾”的定义,才能进行回收吧。书中给出的定义:把分配到堆中那些不能通过程序引用的对象称为非活动对象,也就是死掉的对象,我们称为“垃圾”。

  GC的定义

因为我们期望让内存管理变得自动(只管用内存,不管内存的回收),我们就必须做两件事情: 1. 找到内存空间里的垃圾;2. 回收垃圾,让程序员能再次利用这部分空间 。(《垃圾回收》 P2)只要满足这两项功能的程序,就是GC,不论它是在JVM中,还是在Ruby的VM中。

但这只是两个需求,并没有说明GC应该何时找垃圾,何时回收垃圾等等更具体的问题,各类GC算法就是在这些更具体问题的处理方式上施展手脚。

  GC的历史

John McCarthy身为Lisp之父和人工智能之父,同时,他也是GC之父。1960年,他在其 论文中首次发布了GC算法(其实是委婉的提出)。

《垃圾回收》的作者认为:

从50年前GC算法首次发布以来,众多研究者对其进行了各种各样的研究,因此许多GC算法也得以发布。但事实上,这些算法只不过是把前文中提到的三种算法进行组合或应用。也可以这么说,1963年GC复制算法诞生时,GC的根本性内容就已经完成了。(《垃圾回收》 P4)

那我们常常听说的分代垃圾回收又是怎么回事?作者是这样说的:人们从众多程序案例中总结出了一个经验:“大部分的对象在生成后马上就变成了垃圾,很少有对象能活得很久”。分代垃圾回收利用该经验,在对象中导入了“年龄”的概念,经历过一次GC后活下来的对象年龄为1岁。(垃圾回收》 P141)

分代垃圾回收中把对象分类成几代,针对不同的代使用不同的GC算法,我们把刚生成的对象称为新生代对象,到达一定年龄的对象则称为老年代对象。(《垃圾回收》 P142)

好了,这下我总算知道为什么要分代了,我的总结是: 将对象根据存活概率进行分类,对存活时间长一些的对象,可以减少扫描“垃圾”的时间,以减少GC频率和时长。 根本思路就是对对象进行分类,才能针对各个分类采用不同的垃圾回收算法,以对各算法进行扬长避短。

留一个问题给读者:我们知道分代垃圾回收所采用的堆结构是:

为什么新生代空间要分成“生成空间”和“幸存空间”,而幸存空间又分成两块大小相等的幸存空间1,幸存空间2?

  这些GC算法共同解决的问题

上面我们说了,GC的定义只给出了需求,三种算法都为实现这个需求,那么它们总会遇到共同要解决的问题吧? 我尝试总结出:

  • 如何分辨出什么是垃圾?
  • 如何、何时搜索垃圾?
  • 如何、何时清除垃圾?

这样,只要涉及到垃圾回收,我就可以从这2点需求,3个共同问题(两点三共)出发来讨论、学习。

  如何评价GC算法?

如果没有评价标准,我们当然无法评估这些GC算法的性能。作者给出了4个标准:

  • 吞吐量: 单位时间内的处理能力
  • 最大暂停时间:GC执行过程中,应用暂停的时长。较大的吞吐量和较短的最大暂停时间不可兼得
  • 堆的使用效率:就是堆空间的利用率。可用的堆越大,GC运行越快;相反,越想有效地利用有限的堆,GC花费的时间就越长。
  • 访问的局部性:把具有引用关系的对象安排在堆中较近的位置,就能提高在缓存中读取到想利用的数据的概率。

好吧。两点三共,四标~

  小结

  搞清楚为什么要GC,要实现GC都要解决什么问题,而各类算法又是怎么解决的,最后怎么评价这些算法。GC原来是这么回事。

但是这不是GC的全部。但是提供我一个思考GC的思考框架。

以上就是《垃圾回收的算法与实现》的读书笔记。如果想更深入,可以阅读《垃圾回收算法手册:自动内存管理的艺术》。

from:http://kb.cnblogs.com/page/562433/

程序员用到的各种优秀资料、神器及框架整理

老司机程序员用到的各种优秀资料、神器及框架整理 | IT瘾

成为一名专业程序员的道路上,需要坚持练习、学习与积累,技术方面既要有一定的广度,更要有自己的深度。

笔者作为一位tool mad,将工作以来用到的各种优秀资料、神器及框架整理在此,毕竟好记性不如烂键盘,此项目可以作为自己的不时之需。

本人喜欢折腾,记录的东西也比较杂,各方面都会有一些,内容按重要等级排序,大家各取所需。

这里的东西会持续积累下去,欢迎Star,也欢迎发PR给我。

目录


<!– END doctoc generated TOC please keep comment here to allow auto update –>

资料篇

技术站点

  • 在线学习: CourseraedXUdacityMIT公开课MOOC学院
  • Hacker News:非常棒的针对编程的链接聚合网站
  • Techmeme:美国知名科技新闻和博客聚集网站,类似的还有(Panda, Hacker & Designer News)
  • Programming reddit:同上
  • Java牛人必备: Program Creek
  • stackoverflow:IT技术问答网站
  • GitHub:全球最大的源代码管理平台,很多知名开源项目都在上面,如Linux内核,OpenStack等
  • LeetCode:来做做这些题吧,看看自己的算法水平如何?这可比什么面试宝典强多了。
  • Kaggle,Topcoder: 机器学习、大数据竞赛
  • 掘金:高质量的技术社区
  • 开发者头条
  • InfoQ:企业级应用,关注软件开发领域
  • V2EX: way to explore
  • 国内老牌技术社区:OSChina、博客园、CSDN、51CTO
  • 免费的it电子书: http://it-ebooks.info/
  • 在线学习: http://www.udemy.com/
  • 优质学习资源: http://plus.mojiax.com/
  • 代码练习: http://exercism.io/ and https://www.codingame.com
  • DevStore:开发者服务商店
  • MSDN:微软相关的官方技术集中地,主要是文档类

必看书籍

  • SICP( Structure and Interpretation of Computer Programs)
  • 深入理解计算机系统
  • 代码大全2
  • 人件
  • 人月神话
  • 软件随想录
  • 算法导论(麻省理工学院出版社)
  • 离线数学及其应用
  • 设计模式
  • 编程之美
  • 黑客与画家
  • 编程珠玑
  • The Little Schemer
  • Simply Scheme_Introducing_Computer_Science
  • C++ Prime
  • Effective C++
  • TCP/IP详解
  • Unix 编程艺术
  • 软件随想录
  • 计算机程序设计艺术
  • 职业篇:程序员的自我修养,程序员修炼之道,高效能程序员的修炼
  • 《精神分析引论》弗洛伊德
  • 《失控》《科技想要什么》《技术元素》凯文凯利
  • 程序开发心理学
  • 天地一沙鸥
  • 搞定:无压力工作的艺术

大牛博客

GitHub篇

学习资料篇

Swift相关

客户端

Framework

小工具

游戏

工作、工具篇

优秀项目篇

工具篇

平台工具

  • Phabricator: 软件开发平台,Facebook出品,现已开源,CodeReview神器(从这个往下一直到GitLab之间的工具统统可以忽略了)
  • Redmine/Trac:项目管理平台
  • Jenkins/Jira(非开源):持续集成系统(Apache Continuum,这个是Apache下的CI系统,还没来得及研究)
  • git,svn:源代码版本控制系统
  • GitLab/Gitorious:构建自己的GitHub服务器
  • Postman:RESTful,api测试工具,HTTP接口开发必备神器
  • Sonar:代码质量管理平台
  • Nessus: 系统漏洞扫描器
  • gitbook: https://www.gitbook.io/写书的好东西,当然用来写文档也很不错的(发现不少产品的文档就是用的它)
  • Travis-ci:开源项目持续集成必备,和GitHub相结合, https://travis-ci.org/
  • Trello:简单高效的项目管理平台,注重看板管理
  • 日志聚合:graylog、ELK(推荐新一代的graylog,基本上算作是开源的Splunk了)
  • 开源测试工具、社区(Selenium、OpenQA.org)
  • Puppet:一个自动管理引擎,可以适用于Linux、Unix以及Windows平台。所谓配置管理系统,就是管理机器里面诸如文件、用户、进程、软件包这些资源。无论是管理1台,还是上万台机器Puppet都能轻松搞定。其他类似工具:CFEngine、SaltStack、Ansible
  • Nagios:系统状态监控报警,还有个Icinga(完全兼容nagios所有的插件,工作原理,配置文件以及方法,几乎一模一样。配置简单,功能强大)
  • Ganglia:分布式监控系统
  • fleet:分布式init系统
  • Ansible:能够大大简化Unix管理员的自动化配置管理与流程控制方式。
  • GeoLite免费数据库
  • jsHint:js代码验证工具
  • haproxy: 高可用负载均衡(此外类似的系统还有nginx,lvs)
  • linux OS性能分析工具:dstat,iostat,iotop,nmon
  • kimono:将网页信息转换为api接口的工具
  • 集群管理工具:pdsh,ClusterSSH,mussh(可以用它快速管理Hadoop集群)ipa-server做统一的认证管理
  • influxdb: 分布式时序数据库,结合Grafana可以进行实时数据分析
  • dot: 程序员绘图利器(是种语言,也是个工具)
  • Graph::Easy: (Ascii Art工具)字符流程图绘制,实乃程序员装逼神器。其他类似的工具Asciiflow, vi插件:drawit!
  • spf13-vim: 让你的vim飞起来!
  • Kubernetes: 容器集群管理系统
  • Gatling: 服务器性能压力测试工具
  • systemtap: Linux内核探测工具、内核调试神器
  • Cygwin:Windows下的类UNIX模拟环境
  • MinGW:Windows下的GNU工具集

常用工具

  • Mac下的神兵利器
  • asciinema: 终端录屏神器
  • Fiddler:非常好用的Web前端调试工具,当然是针对底层http协议的,一般情况使用Chrome等自带的调试工具也足够了,特殊情况还得用它去处理
  • Charles: Mac上的Web代理调试工具,类似Fiddler
  • wireshark:知名的网络数据包分析工具
  • PowerCmd:替代Windows Cmd的利器
  • RegexBuddy:强大的正则表达式测试工具
  • Soure Insight:源代码阅读神器
  • SublimeText:程序员最爱的编辑器
  • Database.NET:一个通用的关系型数据库客户端,基于.NET 4.0开发的,做简单的处理还是蛮方便的
  • Navicat Premium:支持MySql、PostgreSQL、Oracle、Sqlite和SQL Server的客户端,通用性上不如Database.NET,但性能方面比Database.NET好很多,自带备份功能也用于数据库定时备份。
  • Synergy : 局域网内一套键盘鼠标控制多台电脑
  • DameWare:远程协助工具集(我在公司主要控制大屏幕用)
  • Radmin: 远程控制工具,用了一段时间的DameWare,还要破解,对Win7支持的不好,还是发现这个好用
  • Listary:能极大幅度提高你 Windows 文件浏览与搜索速度效率的「超级神器」
  • Clover:给资源管理器加上多标签,我平时工作的时候就用它,像Chrome一样使用资源管理器,甚是方便啊(这是Windows平台的)
  • WinLaunch:模拟Mac OS的Launch工具
  • Fritzing:绘制电路图
  • LICEcap:gif教程制作
  • git,svn:版本控制系统
  • Enigma Virtual Box(将exe,dll等封装成一个可执行程序)
  • Open DBDiff(针对SqlServer)数据库同步
  • SymmetricDS:数据库同步
  • BIEE,Infomatica,SPSS,weka,R语言:数据分析
  • CodeSmith,LightSwitch:代码生成
  • Pandoc:Markdown转换工具,出书用的。以前玩过docbook,不过现在还是Markdown盛行啊。
  • Window Magnet[Mac]:增强Mac窗口管理功能,想Win7一样具有窗口拖放到屏幕边缘自动调整的功能
  • log explorer:查看SqlServer日志
  • dependency walker:查询Windows应用程序dll依赖项
  • Shairport4w:将iPhone,iPad,iPod上的音频通过AirPlay协议传输到PC上
  • ngrok:内网穿透工具
  • Axure:快速原型制作工具,还有个在线作图的工具国内的一个创业团队做的,用着很不错 http://www.processon.com/
  • Origami: 次世代交互设计神器
  • 百度脑图: http://naotu.baidu.com/
  • tinyproxy:(Linux)小型的代理服务器支持http和https协议
  • EaseUS Partition Master:超级简单的分区调整工具,速度还是蛮快的,C盘不够用了就用它从D盘划点空间吧,不用重装系统这么折腾哦。
  • CheatEngine:玩游戏修改内存值必备神器(记得我在玩轩辕剑6的时候就用的它,超级方便呢)
  • ApkIDE:Android反编译神器
  • 翻、墙工具(自|由|门、天行浏览器,免费的VPN: http://www.mangovpn.com/),发现最方便还属Lantern,免费用起来超级方便(更新于2015-08-22)
  • 设计工具:Sketch、OmniGraffle
  • MindManger:思维导图
  • MagicDraw:Uml图工具
  • innotop:MySql状态监测工具
  • 墨刀:比Axure更为简单的原型工具,可以快速制作原型
  • Karabiner: Mac专用,修改键盘键位的神器,机械键盘必备
  • Timing:Mac专用,统计你的时间都花在哪了
  • f.lux: 护眼神器,过滤蓝光,程序员护眼必备良品
  • LaTeX: 基于ΤΕΧ的排版系统, 让写论文更方便
  • Antlr:开源的语法分析器,可以让你毫无压力的写个小parser

第三方服务

  • DnsPod:一个不错的只能DNS服务解析提供商
  • DigitalOcean:海外的云主机提供商,价格便宜,磁盘是SSD的,用过一段时间整体上还可以,不过毕竟是海外的,网速比较慢。国内的就是阿里云了。还有个比较知名的是:Linode,据说速度上比DigitalOcean好很多
  • 移动端推送服务:个推、JPush、云巴
  • LeanCloud:移动应用开发服务,包括:数据存储、用户管理、消息推送、应用统计、社交分享、实时聊天等服务
  • Color Hunt: 漂亮炫酷的配色网站,程序员的福音
  • Heroku: PaaS平台

爬虫相关(好玩的工具)

  • Phantomjs(Web自动化测试,服务端渲染等)
  • berserkJS(基于Phantomjs的改进版本)
  • SlimerJS
  • CasperJS
  • selenium
  • HtmlUnit(开源的java 页面分析工具,也是个Headless的浏览器)

安全相关

  • sql注入检测:sqlmap、haviji
  • 端口扫描:nmap
  • 渗透测试:BurpLoader
  • sqltools: sql漏洞利用工具
  • snort: 入侵检测

Web服务器性能/压力测试工具/负载均衡器

  • ab: ab是apache自带的一款功能强大的测试工具
  • curl-loader: 真实模拟、测试Web负载
  • http_load: 程序非常小,解压后也不到100K
  • webbench: 是Linux下的一个网站压力测试工具,最多可以模拟3万个并发连接去测试网站的负载能力。
  • Siege: 一款开源的压力测试工具,可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。
  • squid(前端缓存),nginx(负载),nodejs(没错它也可以,自己写点代码就能实现高性能的负载均衡器):常用的负载均衡器
  • Piwik:开源网站访问量统计系统
  • ClickHeat:开源的网站点击情况热力图
  • HAProxy:高性能TCP /HTTP负载均衡器
  • ElasticSearch:搜索引擎基于Lucene
  • Page Speed SDK和YSLOW
  • HAR Viewer: HAR分析工具
  • protractor:E2E(end to end)自动化测试工具

大数据处理/数据分析/分布式工具

  • Hadoop:分布式的文件系统,结合其MapReduce编程模型可以用来做海量数据的批处理(Hive,Pig,HBase啥的就不说了),值得介绍的是Cloudera的Hadoop分支CDH5,基于YARN MRv2集成了Spark可直接用于生产环境的Hadoop,对于企业快速构建数据仓库非常有用。
  • Spark:大规模数据处理框架(可以应付企业中常见的三种数据处理场景:复杂的批量数据处理(batch data processing);基于历史数据的交互式查询(interactive query);基于实时数据流的数据处理(streaming data processing)),CSND有篇文章介绍的不错
  • 除了Spark,其他几个不错的计算框架还有:Kylin,Flink,Drill
  • Ceph:Linux分布式文件系统(特点:无中心)
  • Storm:实时流数据处理,可以看下IBM的一篇介绍 (还有个Yahoo的S4,也是做流数据处理的)
  • Druid: 实时数据分析存储系统
  • Ambari: 大数据平台搭建、监控利器;类似的还有CDH
  • Tachyon:分布式内存文件系统
  • Mesos:计算框架一个集群管理器,提供了有效的、跨分布式应用或框架的资源隔离和共享
  • Impala:新一代开源大数据分析引擎,提供Sql语义,比Hive强在速度上
  • presto: facebook的开源工具,大数据分布式sql查询引擎
  • SNAPPY:快速的数据压缩系统,适用于Hadoop生态系统中
  • Kafka:高吞吐量的分布式消息队列系统
  • ActiveMQ:是Apache出品,最流行的,能力强劲的开源消息总线
  • MQTT:Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分
  • RabbitMQ:记得OpenStack就是用的这个东西吧
  • ZeroMQ:宣称是将分布式计算变得更简单,是个分布式消息队列,可以看下云风的一篇文章的介绍
  • 开源的日志收集系统:scribe、chukwa、kafka、flume。这有一篇对比文章
  • Zookeeper:可靠的分布式协调的开源项目
  • Databus:LinkedIn 实时低延迟数据抓取系统
  • 数据源获取:Flume、Google Refine、Needlebase、ScraperWiki、BloomReach
  • 序列化技术:JSON、BSON、Thrift、Avro、Google Protocol Buffers
  • NoSql:ScyllaDB(宣称是世界上最快的NoSql)、Apache Casandra、MongoDB、Apache CouchDB、Redis、BigTable、HBase、Hypertable、Voldemort、Neo4j
  • MapReduce相关:Hive、Pig、Cascading、Cascalog、mrjob、Caffeine、S4、MapR、Acunu、Flume、Kafka、Azkaban、Oozie、Greenplum
  • 数据处理:R、Yahoo! Pipes、Mechanical Turk、Solr/ Lucene、ElasticSearch、Datameer、Bigsheets、Tinkerpop
  • NLP自然语言处理:Natural Language Toolkit、Apache OpenNLP、Boilerpipe、OpenCalais
  • 机器学习:TensorFlow(Google出品),WEKA、Mahout、scikits.learn、SkyTree
  • 可视化技术:GraphViz、Processing、Protovis、Google Fusion Tables、Tableau、Highcharts、EChats(百度的还不错)、Raphaël.js
  • Kettle:开源的ETL工具
  • Pentaho:以工作流为核心的开源BI系统
  • Mondrian:开源的Rolap服务器
  • Oozie:开源hadoop的工作流调度引擎,类似的还有:Azkaban
  • 开源的数据分析可视化工具:Weka、Orange、KNIME
  • Cobar:阿里巴巴的MySql分布式中间件
  • 数据清洗:data wrangler, Google Refine

Web前端

  • Material Design: 谷歌出品,必属精品
  • Vue.js: 借鉴了Angular及React的JS框架,设计理念较为先进
  • GRUNT: js task runner
  • Sea.js: js模块化
  • knockout.js:MVVM开发前台,绑定技术
  • Angular.js: 使用超动感HTML & JS开发WEB应用!
  • Highcharts.js,Flot:常用的Web图表插件
  • NVD3: 基于d3.js的图表库
  • Raw:非常不错的一款高级数据可视化工具
  • Rickshaw:时序图标库,可用于构建实时图表
  • JavaScript InfoVis Toolkit:另一款Web数据可视化插件
  • Pdf.js,在html中展现pdf
  • ACE,CodeMirror:Html代码编辑器(ACE甚好啊)
  • NProcess:绚丽的加载进度条
  • impress.js:让你制作出令人眩目的内容展示效果(类似的还有reveal)
  • Threejs:3DWeb库
  • Hightopo:基于Html5的2D、3D可视化UI库
  • jQuery.dataTables.js:高度灵活的表格插件
  • Raphaël:js,canvas绘图库,后来发现百度指数的图形就是用它绘出来的
  • director.js:js路由模块,前端路由,Nodejs后端路由等,适合构造单页应用
  • pace.js:页面加载进度条
  • bower:Web包管理器
  • jsnice:有趣的js反编译工具,猜压缩后的变量名, http://www.jsnice.org/
  • D3.js: 是一个基于JavaScript数据展示库(类似的还有P5.js)
  • Zepto.js:移动端替代jQuery的东东,当然也可以使用jquery-mobile.
  • UI框架:Foundation,Boostrap,Pure,EasyUI,Polymer
  • 前段UI设计师必去的几个网站:Dribbble,awwwards,unmatchedstyle,UIMaker
  • Mozilla 开发者中心: https://developer.mozilla.org/en-US/
  • 图标资源:IcoMoon(我的最爱),Font Awesome, Themify Icons,FreePik,Glyphicons
  • artDialog:非常漂亮的对话框
  • AdminLTE:github上的一个开源项目,基于Boostrap3的后台管理页面框架
  • Respond.js:让不懂爱的IE6-8支持响应式设计
  • require.js: js模块加载库
  • select2:比chosen具有更多特性的选择框替代库
  • AngularUI:集成angular.js的UI库
  • normalize.css: 采用了现代化标准让各浏览器渲染出的html保持一致的库
  • CreateJS:Html5游戏引擎
  • Less,Compass:简化CSS开发
  • emojify.js:用于自动识别网页上的Emoji文字并将其显示为图像
  • simditor:一个不错的开源的html编辑器,简洁高效
  • Sencha: 基于html5的移动端开发框架
  • SuperScrollorama+TweenMax+skrollr:打造超酷的视差滚动效果网页动画
  • jquery-smooth-scroll:同上,平滑滚动插件
  • Animate.css:实现了各种动画效果的css库
  • Emmet:前端工程师必备,ZenCode的前身
  • React: facebook出品的js UI库
  • highlight.js:专门用来做语法高亮的库
  • GoJS: Html5交互式图表库,看demo更适合层次结构的图表。
  • 10 Pure CSS (Mostly) Flat Mobile Devices: http://marvelapp.github.io/devices.css/
  • CodePen: http://codepen.io/
  • jsfiddle: http://jsfiddle.net/ 前端js,html,css测试利器

语言篇

折腾中:Scala、Python、Lua、JavaScript、Go

待折腾:

Scala

Java

  • 常用的IDE:IntelliJ IDEA(强烈推荐),Eclipse,Netbeans
  • fastutil: 性能更好的Java集合框架
  • Guava: 谷歌的Java工具包,应用广泛
  • Curator:Netflix公司开源的一个Zookeeper client library,用于简化Zookeeper客户端编程,现在已经是apache下的一个独立项目了。Spark的HA也用的这货。
  • Rx(Reactive Extensions)框架:Vert.x, RxJava(Android中用的比较多), Quasar
  • FindBugs: 代码静态分析工具,找出代码缺陷
  • Java反编译工具:Luyten,JD-Gui
  • Drools: 规则引擎
  • Jersey: Java RESTful 框架
  • canal: 阿里巴巴出品,binlog增量订阅&消费组件
  • Web开发相关:Tomcat、Resin、Jetty、WebLogic等,常用的组件Struts,Spring,Hibernate
  • Netty: 异步事件驱动网络应用编程框架,用于高并发网络编程比较好(NIO框架,spark 1.2.0就用netty替代了nio)
  • MINA:简单地开发高性能和高可靠性的网络应用程序(也是个NIO框架),不少手游服务端是用它开发的
  • jOOQ:java Orm框架
  • Janino: 超级小又快的Java编译器,Spark的Tungsten引起用的它
  • Activiti:工作流引擎,类似的还有jBPM、Snaker
  • Perfuse:是一个用户界面包用来把有结构与无结构数据以具有交互性的可视化图形展示出来.
  • Gephi:复杂网络分析软件, 其主要用于各种网络和复杂系统,动态和分层图的交互可视化与探测开源工具
  • Nutch:知名的爬虫项目,hadoop就是从这个项目中发展出来的
  • web-harvest:Web数据提取工具
  • POM工具:Maven+Artifactory
  • Akka:一款基于actor模型实现的 并发处理框架
  • EclEmma:覆盖测试工具
  • Shiro:安全框架
  • joda-time:简化时间处理
  • parboiled:表达式解析
  • dozer: 深拷贝神器
  • dubbo: 阿里巴巴出品的分布式服务框架
  • jackson databind: json序列化工具(fastjson,simplejson)
  • Atomikos: 分布式事务管理
  • BoneCP:性能很赞的数据库连接池组件,据说比c3p0快好多
  • ProGuard: obconfuscation tool, 强大的混淆工具
  • S-99:Scala相关的99个问题

Python

  • PyCharm:最佳Python IDE
  • Eric,Eclipse+pydev,比较不错的Python IDE
  • PyWin:Win32 api编程包
  • numpy:科学计算包,主要用来处理大型矩阵计算等,此外还有SciPy,Matplotlib
  • GUI相关:PyQt,PyQwt
  • supervisor:进程监控工具
  • PyGame: 基于Python的多媒体开发和游戏软件开发模块
  • Web框架: Django 开源web开发框架,它鼓励快速开发,并遵循MVC设计

.NET

  • Xilium.CefGlue:基于CEF框架的.NET封装,基于.NET开发Chrome内核浏览器
  • CefSharp:同上,有一款WebKit的封装,C#和Js交互会更简单
  • netz:免费的 .NET 可执行文件压缩工具
  • SmartAssembly:变态的.net代码优化混淆工具
  • NETDeob0:.net反混淆工具,真是魔高一尺道高一丈啊(还有个de4dot,在GitHub上,都是开源的)
  • ILMerge:将所有引用的DLL和exe文件打成一个exe文件
  • ILSpy:开源.net程序反编译工具
  • Javascript.NET:很不错的js执行引擎,对v8做了封装
  • NPOI: Excel操作
  • DotRAS:远程访问服务的模块
  • WinHtmlEditor: Winform下的html编辑器
  • SmartThreadPool:使用C#实现的,带高级特性的线程池
  • Snoop: WPF Spy Utility
  • Autofac: 轻量级IoC框架
  • HtmlAgilityPack:Html解析利器
  • Quartz.NET:Job调度
  • HttpLib:@CodePlex,简化http请求
  • SuperSocket:简化Socket操作,基于他的还有个SuperWebSocket,可以开发独立的WebSocket服务器了
  • DocX:未安装Office的情况下操作Word文件
  • Dapper:轻量级的ORM类,性能不错
  • HubbleDotNet:支持接入数据库的全文搜索系统
  • fastJSON:@CodeProject,高性能的json序列化类
  • ZXing.NET:@CodePlex,QR,条形码相关
  • Nancy:轻量级Http服务器,做个小型的Web应用可以摆脱IIS喽(Nancy.Viewengines.Razor,可以加入Razor引擎)
  • AntiXSS:微软的XSS防御库Microsoft Web Protection Library
  • Jint:JavaScript解释器
  • CS-Script:将C#代码文件作为脚本执行
  • Jexus:Linux下 高性能、易用、免费的ASP.NET服务器
  • Clay:将dynamic发挥的更加灵活,像写js一样写C#
  • DynamicJSON:不必定义数据模型获取json数据
  • SharpPcap:C#版的WinPcap调用端,牛逼的网络包分析库(自带PacketNotNet用于包协议分析)
  • Roslyn:C#,VB编译器
  • ImageResizer: 服务端自由控制图片大小,真乃神器也,对手机端传小图,PC端传大图,CMS用它很方便
  • UI相关:DevExpress, Fluent(Office 07风格), mui(Modern UI for WPF)
  • NetSparkle:应用自动更新组件
  • ConfuserEx: 开源.net混淆工具
  • ServiceStack: 开源高性能Web服务框架,可用于构建高性能的REST服务
  • Expression Evaluator:Eval for C#,处理字符串表达式
  • http://nugetmusthaves.com/
  • Reactive Extensions (Rx):异步,事件驱动编程包, Rx = Observables + LINQ + Schedulers

C & C++

  • Thrift:用来进行可扩展且跨语言的服务的开发(类似的还有个Avro,Google protobuf)。
  • libevent:是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。(对了还有个libev呢)
  • Boost:不多说了,准C++标准库
  • Valgrind/Ptmalloc/Purify: 调试工具
  • NetworkServer架构:acceptor->dispatcher->worker(这个不算工具哦)
  • breakpad:崩溃转储和分析模块,很多crashreport会用到
  • UI界面相关:MFC、BCG和QT这类的就不说了,高端一点的还有Html和DirectUI技术:libcef(基于chrome内核的,想想使用html5开发页面,还真有点小激动呢)、HtmlLayout、Duilib、Bolt,非C++的,还有node-webkit也不错,集成了node和webkit内核。

其他

游戏开发相关

  • MINA:使用Java开发手游和页游服务器(对了还有Netty,也很猛的,都是基于NIO的)
  • HP-Socket:见有有些页游服务器使用这个构建的
  • Unreal: 虚幻引擎,C++,基于这个引擎的游戏很多
  • OGRE:大名鼎鼎的3D图形渲染引擎,天龙八部OL、火炬之光等不少游戏都用了这个引擎
  • OpenVDB:梦工厂C++的特效库,开源的
  • cocos2d:跨平台2D游戏引擎
  • unity3d:跨平台3D游戏引擎,很火的哦
  • Nodejs:也有不少使用它来开发手游和也有服务器(网易的Pomelo)

日志聚合,分布式日志收集

  • Scribe:Facebook的(nodejs + scribe + inotify 同步日志)
  • logstash:强大的日志收集系统,可以基于logstash+kibana+elasticsearch+redis开发强大的日志分析平台
  • log.io: nodejs开发的实时日志收集系统

RTP,实时传输协议与音视频

  • RTP,RTCP,RTSP-> librtp,JRTPLIB(遵循了RFC1889标准)
  • 环形缓冲区,实时数据传输用
  • SDL,ffmpeg,live555,Speex
  • Red5:用Java开发开源的Flash流媒体服务器。它支持:把音频(MP3)和视频(FLV)转换成播放流; 录制客户端播放流(只支持FLV);共享对象;现场直播流发布;远程调用。

from:http://itindex.net/detail/56707-%E8%80%81%E5%8F%B8-%E7%A8%8B%E5%BA%8F%E5%91%98-%E8%B5%84%E6%96%99