一:线程基础
1:进程与线程
2: 线程状态
3: 创建线程
4: 线程中断
5: 安全地终止线程
二:同步
2.1:重入锁与条件对象
2.2: 同步方法
2.3: 同步代码块
2.4:volatile关键字
一:线程基础
1.1.1:什么是进程
进程是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以被看作程序的实体,同样它也是线程的容器。
1.2.1:什么是线程
线程是操作系统调度的最小单元,也叫轻量级进程,在一个进程中可以创建多个线程,这些线程都拥有自己的计数器,堆栈和局部变量等属性,并且能够访问共享的内存。比如QQ的进程,它里面运行了很多子任务,这些子任务有加载图片的、也有处理缓存的,还可能有在后台下载的,这些子任务就是线程。
这时候,==为什么要使用多线程==?一个进程上面运行一个线程不可以吗?
当然是可以的,但是单线程的应用程序处理多任务会显得乏力,比如下载是个耗时的操作,当我们聊QQ的时候,好友发来一张照片,而我们的网络又不能在很短的时间内价值完,只有一个线程的话,就需要等待这张图片价值完成,我们才能进行其它操作,这样的沟通效率就会比较低。使用多线程可以减少程序的响应时间。让加载图片的任务在单独线程执行,这样我们就可以继续操作其挺的任务。
与进程相比,线程创建和切换开销更小,同时多线程在数据共享方面也非常高。
使用多线程能简化程序的结构,使程序便于维护和理解。
1.2:线程的状态
线程的生命周期可能会处于6中不同的状态。分别如下:
- a:New:新创建还没有调用start方法的时候,在线程运行之前可以处理一些基础工作。
- b:Runnable:可运行状态,调用了start方法之后,线程就处于Runnable状态。一个可运行状态的线程,可能正在运行,也可能没有运行,这取决于操作系统给线程分配的运行时间。
- c:Blocked:阻塞状态。表示线程被锁阻塞,暂时不活动。
- d:Waiting:等待状态。线程暂时不活动,这个状态消耗最少,且等待线程调度器激活它。
- e:Timed waiting超时等待状态。指超过一定的时间,自行返回。
- f:Terminated:终止状态。线程执行完毕。导致线程终止的,有两种可能,一是run方法执行结束正常退出。二是抛出没有捕获的异常,导致线程进入终止状态。
线程的状态如图所示:
1.3创建线程
创建线程一般有三种方法,其中有两种方法比较常用,下面看看这些方法都是怎么创建的:
1.3.1. 继承Thread类,重写run()方法
Thread的本质是实现了Runnable接口的一个实列。继承Thread创建线程主要有三步。
- 定义Thread的子类,并重写该类的run()方法,run方法是线程的执行体。
- 创建Thread子类的实列,就创建了线程对象。
- 调用线程对象的start()方法启动线程。
代码如下
public class OneThread extends Thread{
private String TAG = "MyThread";
public void run(){//线程主体
Log.i(TAG , "Thread One");
}
public static void main(String [] args){
Thread thread = new OneThread();
thread.start();
}
}
1.3.2.实现Runnable接口,并实现run()方法
三部曲
- 自定义实现Runnable接口类,实现run()方法。
- 创建Thread类实列,用Runnable接口子类的对象作为参数实列该Thread对象。
- 调用Thread的start()方法启动线程。
public class TwoThread implements Runnable{
private String TAG = "MyThread";
public void run(){//线程主体
Log.i(TAG , "Thread Two");
}
}
//
public class Test1{
public static void main(String [] args){
TwoThread runnableThread = new TwoThread();
Thread thread = new Thread(runnableThread);
thread.start();
}
}
1.3.3. 实现Callable接口,重写call()方法
public class TestCallable {
//创建线程类
public static class MyTestCallable implements Callable {
public String call() throws Exception {
retun "Hello World";
}
}
public static void main(String[] args) {
MyTestCallable mMyTestCallable= new MyTestCallable();
ExecutorService mExecutorService = Executors.newSingleThreadPool();
Future mfuture = mExecutorService.submit(mMyTestCallable);
try {
//等待线程结束,并返回结果
System.out.println(mfuture.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.4.1: 线程中断
在Java早期版本中,可以调用Thread的stop方法来终止线程,其它线程也可以调用该方法,但是现在这个方法已经弃用了。
interrupt方法可以用来请求终止线程,当一个线程调用interrupt方法时,线程的中断状态将被置位。这是每个线程都具有的boolean标志,每个线程都应该不时的检查这个标志,来判断线程是否被中断。
要想弄清线程是否被置位,可以调用Thread.currentThread().isInterrupted():
但是如果一个线程被阻塞,就无法检测中断状态。这是产生InterruptedException的地方。当一个被阻塞的线程(调用sleep或者wait)上调用interrupt方法。阻塞调用将会被InterruptedException中断。
如果每次迭代之后都调用sleep方法(或者其他可中断的方法),isInterrupted检测就没必要也没用处了,如果在中断状态被置位时调用sleep方法,它不会休眠反而会清除这一状态并抛出InterruptedException。所以如果在循环中调用sleep,不要去检测中断状态,只需捕获InterruptedException。
1.5.1: 安全地终止线程
终止线程有两种方式
5.1:用中断来终止线程,代码如下:
public class StopThread{
public static void main(String []args){
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(rannable,"TestRunnable");
thread.start();
TimeUnit.MILLISECONDS.sleep(10);//让线程睡眠10ms,给TestRunnable线程时间来感知中断
thread.interrup();
}
public static class TestRunnable implements Runnable{
private long i;
private final String TAG = "MyThread";
@Override
public void run(){
while(!Thread.currentThread().isInterrupted()){
i++;
Log.i(TAG , "i="+i);
}
Log.i(TAG , "Stop");
}
}
}
还有一种是通过一个boolean变量控制run()方法执行结束,从而回收线程。
代码如下:
public class StopThread{
public static void main(String []args){
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(rannable,"TestRunnable");
thread.start();
TimeUnit.MILLISECONDS.sleep(10);//
runnable.cancel();
}
public static class TestRunnable implements Runnable{
private long i;
private volatile boolean on = true;//volatile是线程同步关键字
private final String TAG = "MyThread";
@Override
public void run(){
while(on){
i++;
Log.i(TAG , "i="+i);
}
Log.i(TAG , "Stop");
}
public void cancel(){
on = false;
}
}
}
二:同步
2.1:重入锁与条件对象
2.2: 同步方法
2.3: 同步代码块
2.4:volatile关键字
2.1:重入锁与条件对象
2.1.1:ReentrantLock锁
private Lock mLock =new ReentrantLock();
mLock.lock();
try{
...//锁执行的区域
}
finally{
mLock.unlock();//执行结束,释放锁
}
2.2:Synchronized关键字定义同步方法
如果一个方法用synchronied关键字声明,那么对象的锁将保护这个方法。也就是说调用该方法,线程必须获得内部的对象锁。
方法定义如下:
public synchronied void testMethod(){
//
}
相当于
public void testMethod(){
this.lock.lock();
try{
//
}finally{
this.lock.unlock();
}
}
2.3:同步阻塞/也叫同步代码块
synchronized(obj){
//执行需要同步的代码
}
2.4:volatile关键字
volatile关键字可以保证变量的可见性,这是java内存管理的特性之一。当一个变量被多个线程使用时,线程A改变了变量的值,为了让其它线程及时更新变量值,可用volatile修饰该变量,这样就可以保证线程之间的可见。