JAVA理论第三章-多线程
3.1 什么是线程?线程和进程的区别?
线程:线程就是一个指令流,是 进程的一个实体,是cpu调度的分派的基本单位,是比 进程更小的可以 独立运行的基本单位。
进程:一个进程可以分到多个线程,具有一定独立功能的程序关于某个数据集合上的一次运行活动,是 操作系统进行资源分配和调度的一个独立单位。
程序由指令和数据组成,指令允许、数据读写都需要将指令加载至CPU,数据加载至内存中。
当一个程序被运行,从磁盘加载这个程序的代码至内存,只是就开启了一个进程
一个线程就是一个指令流,一个进程之内可以分为一到多个线程。
二者对比
- 进程是正在运行程序的实例,进程包含了线程,每个线程执行不同的任务
- 不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间
- 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是一个线程切换道另一个线程)

并行和并发有什么区别?
并发是同一时间应对多件事情的能力,多个线程 轮流使用一个或多个CPU
并行是同一时间动手做多件事情的能力,4核CPU 同时执行4个线程
- 家庭主妇做饭、打扫卫生、喂奶,一个人轮流交替做这件事情,这就是并发
- 家庭主妇雇了个保姆,她们会一起做这些事,这是既有并发,也有并行(这时会产生竞争,例如锅只有一口,一个人用锅时,另一个人就得等待)
- 雇了3个保姆,一个专做饭、一个专打扫卫生、一个专喂奶,互不干扰,这时是并行
3.2 创建线程有几种方式
- 创建Thread类创建线程,实现简单但不可以继承其他类

- 创建Thread类实现Runnable接口并重写run方法。避免了单继承局限性,编程更加灵活,实现解耦

- 创建FutureTask实现Callable接口并重写call方法,创建线程。可以获取线程执行结果的返回值,并且可以抛出异常

- 使用线程池创建(使用java.util.concurrent.Executor接口)

3.3Runnable 和 Callable 的区别?
主要区别:
- Runable接口run方法无返回值;
- Callable接口call方法有返回值,支持泛型
- Runable接口run方法只能抛出运行时异常,且无法捕获处理;
- Callable接口call方法允许抛出异常,可以获取异常信息
3.4 如何启动一个新线程、调用start和run方法的区别?

线程对象调用 run方法不开启线程。 仅是对象调用方法。
线程对象调用 start开启线程,并 让jvm调用run方法在开启的线程中执行。
调用start方法可以启动线程,并且使得线程进入就绪状态,而 run方法只是 thread的 一个普通方法,还是在 主线程中执行。
3.5线程有那几种状态以及各种状态之间的转换?


- 第一是new->新建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
- 第二是Runnable->就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。
- 第三是Running->运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
- 第四是阻塞状态。阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
- (1)等待--通过调用线程的wait()方法,让线程等待某工作的完成
- (2)超时等待--通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- (3)同步阻塞--线程在获取synchronized同步锁失败(因为锁被其他线程所占用),它会进入同步阻塞状态。
- 第五时dead->死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
3.6线程相关的基本方法?
线程相关的基本方法有wait,notify,notifyAll,sleep,join,yield等
1.线程等待(wait)
调用该方法的线程进入 waiting等待状态(一种阻塞状态),只有等待 另外的线程的通知或被中断才会返回,需要注意的是调用wait()方法后, 会释放对象的锁。因此,wait方法一般用在 同步方法或 同步代码块中。
2.线程睡眠(sleep)
sleep导致 当前线程休眠,与wait方法不同的是sleep 不会释放当前占用的锁,sleep(long)会导致线程进入 timed-waiting(超时等待)(一种阻塞状态)状态,而wait()方法会导致当前线程进入wating状态。
3.线程让步(yield)
yield会使当前线程让出CPU执行时间片,与其他线程一起重新竞争CPU时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到CPU时间片,但这又不是绝对的,有的操作系统对线程优先级并不敏感
4.线程中断(interrupt)
中断一个线程,其本意是给整个线程一个通知信号,会影响这个线程内部的一个中断标志位。这个线程本身并不会因此而改变状态(如阻塞,终止等) 并不改变状态
5.join等待其他线程终止
t1.join(t2) 表示等待t2结束后 t1 才能执行
join()方法,等待其他线程终止,在当前线程中调用一个线程的join()方法,则 当前线程转为(超时等待)阻塞状态,回到 另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待CPU的宠幸。
6.线程唤醒(notify)
Object类中的notify()方法, 唤醒在此对象监管器等待的 单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发送,线程通过调用其中一个wait()方法,在对象的监视器上等待,直到当前的线程放弃此对象的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与该对象上主动同步的所有线程进行竞争。类似的方法还有notifyAll(),唤醒再次监视器上等待的所有线程。
notify()和 notifyAll()有什么区别?
notifyAll:唤醒所有wait的线程
notify:只随机唤醒一个 wait 线程
3.7 wait()和sleep()的区别?
1.来自 不同的类
wait():来自 Object类;
sleep():来自 Thread类;
2.关于锁的释放
wait():在等待的过程中 会释放锁
sleep():在等待的过程中 不会释放锁
3.使用的范围
wait():必须在 同步代码块中使用
sleep():可以在 任何地方使用
4.是否需要捕获异常
wait(): 不需要捕获异常
sleep(): 需要捕获异常