Java基础学习总结(104)——多线程、并发、工具类相关的面试题

来源:互联网 发布:mac编辑hosts文件 编辑:程序博客网 时间:2024/05/17 10:04
线程的概念
线程是程序执行的最小单位,也是操作系统调度和分派CPU的最小单元,是进程中的一个实体,是进程中的实际运作单位。可以在一个进程中启动多个线程来完成不同的任务,这些线程共享该进程拥有的资源。
线程进程区别
进程是程序的实体,也是线程的容器,一个进程可以包含多个线程,进程是资源分配的基本单位。
线程属于某个进程,并跟进程中的其他线程共享该进程的资源。同一进程中的线程可以共享相同的内存地址空间,同时每个线程还拥有自己单独的栈内存。
在Java中如何实现线程
在Java语言层面上只有两种实现线程的方式。继承java.lang.Thread类和实现java.lang.Runnable接口。java.lang.Thread代表了一个线程,而java.lang.Runnable代表了线程中运行的任务。
我们应该是使用Runnable还是Thread?
Java不支持多继承,但允许实现多个接口。所以如果需要继承其他类,实现Runnable接口是好了。
题外话,Thread表示一个线程,每个任务都创建一个线程肯定是不妥的,正确的做法应该是初始化一定量的Thread对象,实现Runnable接口创建表示任务的类,并把这些任务对给Thread线程执行。
Thread类的start()和run()方法的区别
start()方法会创建新的线程并启动该线程,所以该方法会调用其他native方法,而run()方法就是正常的Java方法调用,即在原来的线程中执行java代码。
Java中Runnable和Callable的区别
Runnable和Callable都代表要线程中执行的任务。Runnable是JDK1.0加入的,而Callable确实是在JDK1.5加入的。
区别:Callable的 call() 方法可以返回值和抛出异常,
而Runnable的run()方法不能返回值也不能抛出异常。
Callable是需要使用java.util.concurrent.ExecutorService.submit(Callable<T>)方法提交的,这样就可以获得Future对象,该对象可以装载了Callable接口的call()方法的返回结果。
Java中CyclicBarrier和CountDownLatch的区别
CyclicBarrier和CountDownLatch都可以协同多个线程,让指定数量的线程等待其他所有的线程都满足某些条件之后才能继续执行。CyclicBarrier可以重复使用,而CountdownLatch只能使用一次,如果还需要该能够,就只能重新new一个CountdownLatch对象。
另外一个是CountdownLatch每次调用await()方法之前都需要先调用countDown()方法,而CyclicBarrier不需要。
简述Java内存模型
Java内存模型包含了一系列的规则和指导原则。
Java语言是跨平台的,Java的内存模型确保了Java在不同的操作系统、CPU、内存架构上有确定的行为,特别是在多线程的情况下,一个线程所做的变动对其他线程是否可见是很重要的,这叫做先行发生关系:
代码是按照先后顺序执行的,就是所谓的程序次序规则。
对同一个锁的解锁操作肯定比后续的加锁操作先发生,也就是所谓的管程锁定规则。
A write to a volatile field happens-before every subsequent read of that same field, known as Volatile variable rule.
对同一个volatile变量的写操作肯定比后续的读操作先发生,也就是所谓的volatile变量规则。
Thread.start()在线程的其他操作发生之前被调用,也就是所谓的线程启动规则。
一个线程中断了其他线程的操作比被中断线程检查到中断先发生,也就是所谓的线程中断规则.
构造对象肯定比该对象被终结先发生,也就是所谓的对象终结规则.
A比B先发生,B比C先发生,那么A肯定比C先发生,也就是说顺序有传递性。
Java中的volatile变量有什么特点
volatile是一个变量修饰符,有且只有成员变量才能用它修饰。多线程中如果缺少同步,一个成员变量被修改之后不一定会被其他线程可见,如果对该成员变量加上volatile修饰,那么就可以保证下一次读操作一定会在上一次写操作之后发生。
线程安全的概念,Vector是否是线程安全类?
有多个线程同时运行代码,如果每次运行的结果都跟单线程运行结果一样,各种变量的值也一直跟预期的一样,这就可以称这段代码是线程安全的,记住是一直,某次跟单线程运行结果一样是不行的。
集合类根据线程是否安全可以分为两类,线程安全和非线程安全。Vector是使用同步方法来实现线程安全的,在JDK1.1的时候引入的 而和它类似的的ArrayList是在JDK1.2引入的,并且不是线程安全的。
竞态条件的概念
多个线程对共享的数据同时进行读写的时候,最终的结果取决于这些线程的执行顺序,而如果执行顺序不正确就会出现错误结果,这就是所谓的静态条件。
Java停止线程的方法
首先必须要明确Java本身没有提供停止线程的方法。特别值得一提的是JDK1.0中的一些控制方法,比如stop(), suspend() 和 resume()等,由于存在潜在的死锁问题因此在后续的版本中被弃用了。当run() 或call() 方法执行完线程就会自动结束,如果确实需要手动结束某个线程,可以使用一些变量来标志是否退出run()或call(),如果没有采用同步机制,则需要使用volatile修饰,还可以通过取消任务来中断线程,即通过判断是否中断。
线程发生异常的结果
如果异常没有被捕获,则该线程会终止执行。
注意:
Thread静态方法
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
和实例方法
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
设置了可以抓取线程没有被捕获的异常,但是线程还是会终执行的。
如何在线程之间共享数据
共享对象(类的静态变量或类的实例变量)
也可以使用阻塞队列等并发的集合。
notify和notifyAll的区别
notify()方法只能随机唤醒某个正在等待的线程;
notifyAll()可以唤醒所有正在等待的线程,变成等待该对象上的锁,如果该对象已经被解锁,它们就会去一起去竞争该对象的锁。notifyAll()能够确保至少有一个线程能继续运行。
注意:
当有多个线程在等待的时候,必须在线程被唤醒并执行完操作之后继续调用notify()或notifyAll(),以便唤醒其他正在等待的线程。
wait,notify和notifyAll是Object类的方法的原因
Java提供的是对象级的锁而不是线程级的锁,这样每个对象都有锁。调用对象中的wait()方法意思就是在等待该对象的锁,而不是某个线程的锁,如果wait()方法定义在Thread类中,就等于只有线程才能有锁,而其他任何对象不可以有锁。
wait,notify、notifyAll都是锁级别的操作,把他们定义在Object类中是因为锁是属于对象的。
ThreadLocal变量含义(线程局部变量)
ThreadLocal的对象即使被多个线程共享,每个线程设置进去的值都不会相互干扰,而且自己只能访问到自己设置的值,这样相同的对象相同的方法调用却各不一样,就好像每个线程在ThreadLoad上都拥有自己独立的一个变量
为了保证线程安全,某些变量不应该被多个线程共享;但是如果这些变量(比如SimpleDateFormat)的创建是非常昂贵的,那么就应该整个线程都共享该变量,这时候有什么办法能够做到让某个非静态变量可以被某个线程的所有方法都访问到呢?很难,什么办法都很难做到。而使用ThreadLocal就可以做到。
首先,通过整个线程复用某些变量减少了代价昂贵的对象的创建。
其次,没有使用高代价的同步或不变性就获得了线程安全。这里需要解释一下,如果某个对象(比如电话簿,有增删功能)一定需要被所有的对象共享,那么为了线程安全肯定必须使用同步机制。但是如果只是需要在某个线程内共享,那么怎么办到?没有办法,只能使用静态变量,可是静态变量不可以一个线程一个,因为我们不知道会有多少个线程,所以只能采取一个静态变量被所有的线程共享,这就需要同步。
JDK中有ThreadLocalRandom类,它减少了在多线程环境中创建代价高昂的Random对象的个数。
0 0