一、线程概述
线程是在程序中独立并发的执行流,相比于进程,线程具有更高的性能,多个线程可以共享一个进程虚拟空间,线程之间共享内存非常方便,可以拥有很高的并发。
二、线程的创建和启动
2.1 继承Thread类创建线程类
通过继承Thread类来创建启动多线程的步骤如下:
(1)定义Thread的子类,重写run方法
(2)创建Thread子类的实例
(3)通过start来启动线程
Thread.currentThread()可以返回当前正在执行的线程对象
getName()可以返回线程的名字
public class FirstThread extends Thread{
public void run()
{...}
}
new FirstThread().start();
2.2 实现Runnable接口创建线程类
通过Runnable接口来创建并启动多线程的步骤如下:
(1)定义Runnable的实现类,并且重写run方法
(2)创建Runnable实现类的实例,并且以此实例作为Thread的target来创建Thread对象
public class SecondThread implements Runnable
{
public void run()
{...}
}
new Thread(st,'name').start();
采用Runnable接口的方式来创建的多条线程可以共享线程类的实例属性。
采用Runnable接口方式来创建的线程只能通过Thread.currentThread()来获取当前线程,而不能通过this
3 线程的生命周期
- 新建状态:new了一个线程之后
- 就绪状态:调用了start之后(永远不要直接调用run方法)
- 运行状态:线程获得CPU,开始运行run方法之后
- 阻塞状态:调用sleep,调用阻塞式IO方法,等待通知,程序调用了suspend方法等,都会使线程进入阻塞状态。
- 线程死亡:run()执行完毕,抛出未捕获的Exception和Error,或者调用该程序的stop()方法来结束该线程。
可以通过线程的isAlive方法来判断线程是否死亡
不要试图对一个已经死亡的线程调用start方法
4 控制线程
4.1 join线程
某个线程调用另一个线程的join方法以后,该线程将被阻塞,直到被join的线程执行完毕以后为止。
- join()
- join(long millis)
- join(long millis,int nanos)
4.2 后台线程
后台线程有个特征:如果前台线程都死亡,则后台线程会自动死亡。
通过调用Thread的setDaemon(true)的时候,就可以将其设为后台线程。
可以通过isDaemon()来判断是否是后台线程
4.3 线程睡眠Sleep
- Thread.sleep(long millis)
- Thread.sleep(long millis, int nanos)
4.4 线程让步yield
yield方法不会阻塞该线程,而是转入就绪状态,让线程调度器重新调度一次。
- Thread.yield
4.5 改变线程优先级
- Thread.setPriority(int newPriority)
优先级值在1~10之间,也可以使用MAX_PRIORITY(10),MIN_PRIORITY(1),NORM_PRIORITY(5)
5 线程的同步
5.1同步代码块
synchronized(obj){ 同步代码块 }
5.2同步方法
public synchronized void draw(double drawAmount)
5.3释放同步监视器的锁定
以下情况会释放:
- 代码块执行完毕
- break return
- 抛出错误或者异常
- 程序执行了同步监视器对象的wait()方法
5.4 同步锁
常用的有可重用锁ReentrantLock,可重用的意思是说,对已经加锁的ReentrantLock可以再次上锁,该对象会维持一个计数器来记录。
public class Accout
{
private final ReetrantLock lock = new ReentrantLock();
public void draw(double drawAmout)
{
lock.lock();
try
{
//临界区
}
finally
{
lock.unlock();
}
}
}
5.5 死锁
当两个线程相互等待对方释放同步监视器就会发生死锁。
6 线程通信
6.1 线程的协调运行
Object类提供的wait(), notify(),notifyAll()三个方法,必须由同步监视器来调用:
- 对于使用synchronized修饰的同步方法,该类的默认实例this就是同步监视器
- 对于使用synchronized修饰的代码块,后面括号里的对象就是同步监视器
6.2 使用条件变量进行控制协调
如果程序不使用synchronized关键字来保持同步,而是直接使用Lock对象来保持同步,则系统中不存在隐式的同步监视器对象。Java提供了一个Condition类来保持协调。
- await()
- signal()
- signalAll()
public class Account
{
private final Lock lock = new ReentrantLock();
private final Condition cond = lock.newCondition();
####6.3 使用管道流
如果两条线程之间需要更多的信息交互,则可以考虑使用管道流。
PipedWriter pw = null;
PipedReader pr = null;
try
{
pw = new PipedWriter();
pr = new PipedReader();
pw.connect(pr);
new WriterThread(pw).start();
new ReaderThread(pr).start();
}
###7 Callable和Future
Callable看起来像是Runnable的接口和增强版,call方法是其线程执行体,但是比run()方法更强大:可以有返回值,可以抛出异常
class RtnThread implements Callable<Integer>
{
public Integer call(){
return i
}
}
public class CallableTest
{
public static void main(String[] args)
{
RtnThread rt = new RtnThread();
FutrueTask<Integer> task = new FutureTask<Integer>(rt);
new Thread(task,'name').start();
System.out.println(task.get());
}
}
###8 线程池
略过
###9 线程相关类
####9.1 ThreadLocal类
ThreadLocal类是线程局部变量的意思,就是为每一个使用该变量的线程都提供一个变量值的副本。从而避免冲突。
ThreadLocal提供了三个public方法:
* T get()
* void remove()
* void set(T value)
>如果需要进行多个线程之间的资源共享,就需要使用同步机制,如果只是需要隔离多个线程之间的共享冲突,则可以使用ThreadLocal
####9.2 线程包装不安全的集合
Java集合中介绍的ArrayList,LinkedList, HashSet, TreeSet, HashMap都是线性不安全的。可以使用Collections的方法来将其变成线程安全:
SynchronizedCollection/List/Map/Set/SortedMap/SortedSet
####9.3 线程安全的集合类
JDK1.5开始,java.util.concurrent包下提供了ConcurrentHashMap, ConcurrentLinkedQueue。