一、多线程基础
进程
一个任务称为一个进程
进程和线程的关系是:一个进程可以包含一个或者多个线程,至少会有一个线程。
多进程的缺点:
- 创建进程比创建线程开销大。
- 进程间通信比线程间通信要慢,因为线程间通信就是读写同一个变量,速度很快。
优点:多进程稳定性比多线程高,因为在多进程的情况下。一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃回直接导致整个进程崩溃。
二、创建新线程
创建新线程,我们需要实例化一个Thread实例,然后调用它的start()方法启动线程:
1、通过继承Thread来创建线程
2、实现Runnable接口创建线程
通过实现Runnable接口来创建
三、线程的状态
JDK中用Thread.State类定义了线程的几种状态
一个线程的完整生命周期通常会经历以下五个状态:
新建:当一个Thread类及子类的对象被声明并创建时,这个线程处于新建状态;
就绪:处于新建的线程被启动线程(start())后,就会进入线程队列中等待CPU分配时间片,此时已具备运行的条件;
运行:当就绪的线程被调度并获得CPU资源后就会进入运行状态,run()方法定义了线程的操作和功能;
阻塞:在特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己执行,此时进入阻塞状态;
死亡:当线程执行完了全部的工作或者被提前强制中止或者出现异常而导致结束。
线程终止的原因:
- 线程正常终止:run()方法执行到return语句返回;
- 线程意外终止:run()方法因为没有捕获的异常导致线程终止;
- 对某个线程的Thread实例调用stop()方法强制终止;
Java线程对象Thread的状态包括:New、Runnable、Blocked、Waiting、Timed Waiting、
Terminated。
New:新创建的线程,还没有执行;
Runnable:运行中的线程,正在执行run()方法的Java代码;
Blocked:运行的线程,因为某种操作而被阻塞而挂起;
Waiting:运行中的线程,因为某些操作而在等待中;
Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
Terminated:线程已终止,因为run()方法执行完毕。
通过另一个线程对象调用join()方法能等待其执行结束。
四、线程中断
中断线程就是其他的线程给该线程发出信号,该线程收到后结束执行run()方法,使自身的线程能够立即结束运行。
中断线程需在其他线程中对目标线程调用interrupt()方法,目标线程需检测自身是否是interrupt()状态,假如是则立即结束运行。
volatile关键字的目的是告诉虚拟机:
-
-
- 每次访问变量时,总是获取主内存的最新值;
- 每次修改变量后,立刻回写到主内存。
-
五、守护线程
在调用start()方法前,调用setDaemon(true)把线程标记为守护线程:
Thread th = new MyThread();
Th.setDaemon(true);
Th.start();
守护线程中,编写代码要注意:守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。
守护线程又称为后台线程,默认创建的线程都是普通线程,或者称为前台线程,线程提供了一个方法:
void setDaemon(boolean on)只有调用该方法并且传入参数为true时,该线程才会被设置为守护线程。守护线程在使用上与普通线程没有差别,但是在结束时会有一个差别,即:线程结束时,所有正在运行的守护线程都会被强制停止进程结束:当一个进程中所有的普通线程都结束时,进程才会结束
六、线程同步
Java程序使用`synchronized`关键字对一个对象进行加锁:
synchronized(lock){
n = n + 1;
}
`synchronized`保证了代码块在任意时刻最多只有一个线程能执行
如何使用synchronized:
- 找出修改共享变量的线程代码块;
- 选择一个共享实例作为锁;
- 使用synchronized(lockObject){……}
用`synchronized`修饰方法可以把整个方法变为同步代码块,`synchronized`方法加锁对象是`this`。
多线程并发的安全问题:
产生:当多个线程并发操作时,由于线程切换实际的不确定性,会导致操作资源的代码;
顺序为按照设计顺序执行,出现操作混乱的情况,严重时可能导致系统瘫痪;
解决:将并发操作同一资源改为同步运行,即:有先后顺序的操作。
同步与异步:
同步:程序运行有先后顺序;
异步:程序运行没有先后顺序;
七、Atomic
`AtomicInteger`提供的主要操作有:
-
-
- 增加值并返回新值:`int addAndGet(int delta)`
- 加1后返回新值:`int incrementAndGet()`
- 获取当前值:`int get()`
- 用CAS方式设置:`int compareAndSet(int expect, int update)`
-
Atomic类是通过无锁(lock-free)的方式实现的线程安全(thread-safe)访问。
八、线程池
其作用为:重复利用资源;
`用ExecutorService`接口表示线程池,用法:
ExecutorService executor = Executor.newFixedThreadPool(2);
executor.sub(text1);
……
该接口的实现类有:
FixedThreadPool:线程数固定的线程池;
CachedThreadPool:线程数根据任务动态调整的线程池;
SingleThreadExecutor:仅单线程执行的线程池。