线程详解

           在了解线程之前我们先来看看并行和并发!

并发(Concurrent):在同一时间间隔内交替做多个事件,多线程是并发的一种形式。

并行(Parallel):在同一时刻做多个事件。

 

并发:在一台处理器上“同时”处理多个任务;并行:在多台处理器上同时处理多个任务。

所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

有一个清晰地比喻:
并发:一个人同时吃三个苹果。

并行:三个人同时吃三个苹果。

 

线程和进程的区别?

进程是程序运行和资源分配的基本单位,

线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位;

一个程序至少有一个进程,一个进程至少有一个线程;

同一进程中的多个线程之间可以并发执行;

 

 创建线程有哪几种方式?

①. 继承Thread类创建线程类

 

  • 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
  • 创建Thread子类的实例,即创建了线程对象。
  • 调用线程对象的start()方法来启动该线程。

②. 通过Runnable接口创建线程类

  • 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  • 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  • 调用线程对象的start()方法来启动该线程。

 

③. 通过Callable和Future创建线程

 

  • 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  • 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  • 用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

runnable 和 callable 的区别

 

  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

 

 

线程有哪些状态?

线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。

 

  • 创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
  • 就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,
  •                   但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。
  •                   在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
  • 运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
  • 阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可                    以导致线程阻塞。
  • 死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪

 sleep() 和 wait() 有什么区别

  • sleep():方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。
  • wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程

notify()和 notifyAll()有什么区别?

notify()方法只随机唤醒一个 wait 线程

 notifyAll()方法唤醒所有 wait 线程

 

线程的 run()和 start()有什么区别

 start()方法来启动一个线程,真正实现了多线程运行。

run(),其实就相当于是调用了一个普通函数而已

 

 创建线程池有哪几种方式?

①. newFixedThreadPool(int nThreads)

创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。

②. newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。

③. newSingleThreadExecutor()

这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。

④. newScheduledThreadPool(int corePoolSize)

创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

 

线程池中 submit()和 execute()方法有什么区别?

  • 接收的参数不一样
  • submit有返回值,而execute没有
  • submit方便Exception处理

  在 java 程序中怎么保证多线程的运行安全?

线程安全在三个方面体现:

  • 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
  • 可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
  • 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

 

posted @ 2019-08-02 23:23  长木木弓  阅读(229)  评论(0编辑  收藏  举报