并发编程三个必要因素是什么:
- 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么
全部执行失败。 - 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
- 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
Java 程序中怎么保证多线程的运行安全
- 线程切换带来的原子性问题 解决办法:使用多线程之间同步synchronized或使用锁(lock)。
- 缓存导致的可见性问题 解决办法:synchronized、volatile、LOCK,可以解决可见性问题
- 编译优化带来的有序性问题 解决办法:使用volatie关键字禁止指令重排
创建线程的四种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 使用匿名内部类
Runnable和Callable接口的区别
- Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛型,和Future、
FutureTask配合可以用来获取异步执行的结果 - Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出
异常,可以获取异常信息 注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,
此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用run() 方法?
直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。
线程的状态:新建、就绪、运行、阻塞、死亡
java的线程调度算法:分时调度,抢占式
sleep() 和 wait() 有什么区别?
- sleep() 是 Thread线程类的静态方法,wait() 是 Object类的方法。
- sleep() 不释放锁;wait() 释放锁。
- Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。
- wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用wait(longtimeout)超时后线程会自动苏醒。
你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?
处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。所以应该使用循环调用
为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?
因为Java所有类的都继承了Object,Java想让任何对象都可以作为锁,并且 wait(),notify()等方法用于等待对象的锁或者唤醒线程,在 Java 的线程中并没有可供任何对象使用的锁,所以任意对象调用方法一定定义在Object类中。
为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?
在使用wait()方法的时候会释放对象锁,使用notify()和notifyAll()也会释放锁,所以说必须持有锁才能释放锁,所以只能在同步代码块中
Thread 类中的 yield 方法有什么作用?
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。
为什么 Thread 类的 sleep()和 yield ()方法是静态的?
因为他们只能在运行中的线程中使用,如果是非静态的,那么未运行的线程也就可以调用了,就会出现错误
线程的 sleep()方法和 yield()方法有什么区别?
- sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
- 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
- sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;
- sleep()方法比 yield()方法(跟操作系统 CPU 调度相关)具有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。