创建线程

宏观地看,总归要new Thread。当然参数里会传Runnable,但还是要显式的创建线程。
Runnable可以理解成新的线程要执行的内容,但不能理解成新线程本身。

想让新的线程运行就用创建的线程的start方法,执行后jvm会自动地去调用run方法。

想要自定义新线程执行的内容,可以通过自定义一个继承Thread类的类来实现,也可通过自定义类实现runnable接口的方式。
继承Thread类覆盖run方法比较简单,但是局限性大,一般不这么做。
实现接口的方法比较常用。

生命周期

还是五个状态,新建态,结束态,然后运行、就绪、阻塞三态。已经是老八股文了。

常用方法

sleep 一看就知道怎么用。好玩的是就算你在run里想用sleep,也不能在run里抛异常。必须try catch掉。这是因为子类覆盖的方法不能抛出比父类更大的异常。父类没抛异常,所以子类不能抛。

join 表示当前线程等待调用这个方法的线程。

currentThread 这个方法常用在runnable方式的run方法里。你把这个代码写在某个线程自己的函数里,当然获取到的就是自己。

setDaemon 设置守护线程 守护线程在其他线程结束之后会自动结束

线程同步

多线程同时访问一个资源时,可能造成资源的覆盖和不一致问题。可能对线程进行通信和协调,这叫做线程同步。

为了实现同步,可以加锁。(当然你加了锁,多线程可能就会变得没太大意义了)
加锁通常是说用synchronized代码块把业务代码锁住。synchronized代码块需要锁一个对象。如果这个对象不唯一,那多线程环境下业务代码就不能保证同一时间只有一个线程执行。为了让被锁住的对象只有一个,可以考虑让这个对象是static的。

死锁

造成死锁的四个条件
1、互斥: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。

解决方法
死锁预防(破坏4个条件中的一个)
死锁避免(银行家算法)
死锁检测与解除 (发现死锁了再解决)

Lock

各方面基本都比synchronized好,就是你自己要记得释放。。。

wait 和 notify

可以用于线程间通信。或者更具体的例子是多个线程交替打印。
调用wait自动释放锁。
wait和notify一定要在锁定的代码块里用。

synchronized(this) {
    notify();
    dosth();
    wait();
}

生产者消费者

经典模型了,生产满了或者消费没了就wait。否则notify。

Java5 Callable

和runnable一个概念。区别在于有返回值。所以一般是需要返回值的时候用callable。另外要注意,Callable有泛型。
要获取Callable实现类的call方法返回的结果,需要FutureTask类对象(作为Runnable实现类传到Thread的构造函数里)的get方法。

线程池(简单提一嘴)

线程池就是提前创建好一些线程,避免频繁地创建线程和销毁线程的开销。
在线程池里布置任务用submit方法,把要执行的东西传进去就行了。