目录
进程与线程
进程 --- 是一种“自包容”的应用程序,拥有自己的独立内存地址空间。
线程 --- 是在进程内部的再次划分,一个进程可以包含多个线程,这些线程是共享进程的内存地址空间。
我们的操作系统在同时运行多个程序,每一个程序就是一个进程;一个程序也可以同时完成多个执行路径,每一个执行路径就是一个线程。(GC就是在JVM中的一个线程,我们写的程序从main开始到main结束,也是JVM中的一个线程。)
多线程的同时执行,并不是绝对意义的同时。而是由CPU在多个线程之间进行来回的切换,只是它切换的速度很快,我们感觉不到它的停顿,所以认为是在同时执行。
线程的生命周期
线程的生命周期一共是5个状态
新建状态 --- new出来一个线程对象
就绪状态 --- 启动线程,但是在线程执行自己的内部代码之间。(在启动(start)和 运行(run)之间)
运行状态 --- 线程执行自己内部的代码
中断状态 --- 发生在运行过程中,这个线程由于某种原因没有被CPU执行。中断状态又有多个原因照成:由于线程优先级的原因没有抢到CPU;我们也可以让某个线程主动休眠(sleep);线程也有主动退出和换回的行为;线程在执行输入或输出的时候进入阻塞状态。
死亡状态 --- 线程执行完自己的内部代码,然后停下来开始回收自己的资源,进入自毁。
线程的代码实现
在JDK当中有1个类和两个接口用来进行多线程编程的:Thread, Runnable,Callable。其中Callable是在JDK1.5之后提出来增加Runnable的能力的。
Thread类的使用
1、我们可以通过继承Thread,然后重写它当中的run()方法来实现我们自己的线程类。 run()方法,我们可以把它想象成是这个线程的主方法。
2、在另一个线程当中,我们产生它的对象,然后调用它的start()。 注意 如果我们直接调用run(),不会报错。但相当于在做普通的方法调用,这个时候并没有启动一个新的线程。调用start()方法,才会启动新线程,并在新线程中执行run()当中的代码。
3、Thread类常用方法 sleep() --- 静态方法,用来让当前线程进入休眠状态。参数是毫秒数。一旦休眠时间到了,那么该线程会重新参与CPU的竞争。
setName() getName() --- 非静态方法,给线程对象设置名字或获取名字
setPriority() getPriority() --- 非静态方法,给线程设置优先级。总共1-10,默认为5。优先级高的不是一定比优先级低的先执行,只是它获取到CPU的几率更大而已。
Runnable接口
如果一个类已经有一个父类了,又想同时作为线程类,由于Java是单继承的,所以它不可能再同时继承Thread类了。所以,又提供了Runnable接口。
1、书写一个线程类实现Runnable接口,重写run()方法;
2、在开启它的线程中,把这个Runnable的实现类对象,传给一个Thread对象(在构造方法进行传参),然后调用Thread对象的start()。
Callable接口(返值)
无论是继承Thread的方式,还是实现Runnable的方式,都需要重写run()方法。但是run()在设计之初有两个弱点:
1、run()没有返回;
2、run()都不能抛异常。
所以在JDK1.5的时候提出了新的Callable接口,该接口的call()方法,可以有返回,也可以抛异常。
1、定义Callable的实现类,然后重写call方法。可以通过Callable的泛型,指定call()的返回类型;
2、在主线程中产生一个FutureTask对象,传入Callable实现类对象;
3、然后再new 出一个Thread对象,传入FutureTask对象;
4、调用Thread的start()方法,执行线程;执行完以后,调用FutureTask对象的get方法获取子线程返回来的数据。
线程安全性
线程安全性发生的时机:当多个线程同时操作统一“资源对象”的时候,才有可能会发生线程安全问题。
解决方案:
线程同步。我们可以在资源对象身上加一把锁(同步锁),然后让一个线程先执行,如果他没有执行完,别的线程只能进入等待。当它执行完了以后,会放开资源,然后下一个线程才进入执行。--- synchronized关键字
同步方法
当多个线程调用同一个资源对象的方法的时候,为了解决线程安全,我们给这个方法添加一个“synchronized”修饰符,让这个方法称为“同步方法”。
成为同步方法之后的效果是:当一个线程执行这个方法的时候,别的线程只能等着,直到这个方法被该线程执行结束。然后下一个线程再去执行,一个一个来。
注意: 此时synchronized关键字是加在资源类的方法身上。
同步块
当多个线程访问统一资源对象的时候,我们不在资源对象身上加同步,而是让线程内的调用处进行选择是否加同步锁进行调用。如果要加,那么就书写“同步块”。
synchronized(资源对象){
资源对象.行为1();
资源对象.行为2();
}