多线程篇

发布时间 2023-04-12 19:31:02作者: XIAOBAI001

1.Java中实现多线程的几种方法

  • 继承Thread类

  • 实现Runnable接口

  • 实现Callable接口

  • 线程池方式创建

2.使用Thread、Runnable和Callable创建线程的优缺点

  • 采用继承Thread类的方式创建线程的优缺点

    • 优点:直接使用this即可获取当前线程,编程简单

    • 缺点:已经继承了Thread类,无法再继承其他父类了

  • 采用实现Runnable、Callable接口的方式创建线程的优缺点以及区别

    • 优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以给共享一个target对象,所以比较适合多个相同线程来处理同一份资源的情况。

    • 缺点:编程较为复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。

    • 区别:

      • Runnable通过重写run方法实现,Callable通过call方法实现

      • Runnable可以提交给Thread来包装下直接启动一个线程来执行,Callable一般都是提交给ExcuteService来执行

      • Runnable的任务无返回值,Callable的任务执行后有返回值

      • call方法可以抛出异常,run方法不可以

      • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。

3.如何停止一个正在运行的线程

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  • 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法
  • 使用interrupt方法中断线程

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

  • sleep()方法属于Thread类中的;而wait()方法属于Object类中的。
  • sleep()方法导致程序暂停执行指定的时间,让出cpu给其他线程,但是它的监控状态依然保持着,当指定时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁;而调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

5.volatile 是什么?可以保证有序性吗?

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰后,那么保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。 什么叫保证部分有序性,即当程序执行到volatile变量的读写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。 使volatile一般用于状态标记量和单例模式的双检锁。

 

6.为什么wait和notify方法要在同步块中调用?

  • 只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方法

  • 如果不这么做会抛出IllegalMonitorStateException异常

  • 避免wait()和notify()之间产生竞态条件 wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此线程必须在某个对象的同步方法或同步代码块中才能够调用该对象的wait()方法。 在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁,因此,必须在某个对象的同步方法或同步代码块中才能够调用该对象的notify()和notifyAll()方法。 调用wait()方法的原因通常是调用线程希望某个特殊的状态或变量被设置之后再继续执行。 调用notify()和notifyAll()方法的原因通常是调用线程希望告诉其他等待线程"特殊状态已被设置"。 这个状态作为线程间通信的通道,他必须是一个可变的共享状态或变量。