面试-Java多线程-3

9. 线程面试

 

9.1什么是线程

线程是操作系统能进行运算调度的最小单位,可以使用多线程对运算密集型任务进行任务提速

 

9.2线程和进程区别

1) 线程是进程的子集,一个进程可以有多个线程,每条线程并行执行不同的任务。

2) 不同进程使用不同的内存空间,所有的线程共享一片相同的内存空间。

每个线程还拥有单独的栈内存用来存放本地数据

 

9.3java如何实现线程

1) 继承Thread类

2) 实现Runable接口

 

9.4用Runable还是Thread

如果类要实现多继承,那最好使用Runable

 

9.5Thread类的start和run的区别

1) start方法是被用来创建新线程,而start内部调用了run方法。

2) 如果直接调用run方法,只会在原来的线程中调用,没有启用新的线程

3) run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次

 

9.6java中Runable和Callable的区别

1) 两者都表示在不同线程中执行的任务。

2) Callbale的call方法可以返回值和抛出异常,并且可以返回装载有计算结果的Future对象

 

9.7java中的CycliBarrier和CountDownLatch

1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同;

2)CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

3) CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的

 

9.8java内存模型

1) 规定和指引java程序做不同的内存架构、cpu、操作系统时间有确定性的行为。

2) java内存模型对一个线程所做的变动能被其他线程可见提供了保障,他们之间是先行发生关系

 

9.9☆java中的volatitle

Volatitle是Java虚拟机提供的轻量级的同步机制,保证可见性,不保证原子性,禁止指令重排

原理:

如果对声明了volatitle的变量进行写操作,jvm就会向处理器发送一条lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。在多处理器下,为了保证各个处理器的缓存一致性,每个处理器通过嗅探在总线上传播来检查自己的缓存值是不是过期,当处理器发现自己的缓存对应的内存地址被修改,就将当前处理器的缓存设置成无效状态,当处理器对这个数据进行修改操作时,会重新从系统内存中把数据读到处理器缓存中。总的来说volatitle两条实现原则:1.lock前缀的指令会引起处理器缓存写回到内存中,2.处理器的缓存写回到内存中会导致其它处理器缓存失效

 

1) volatitle是一个特殊修饰符,只有成员变量能使用。

2) java并发程序缺少同步类的情况下,对于成员变量的操作对其他线程是透明的。

3) volatitle变量可以保证下一个读取操作是在上一个写操作之后

 

l 为什么volatitle不保证原子性

原子性是指不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割,需要整体完整要么同时成功,要么同时失败

答:多个线程并发执行将工作内存中的值写回主内存中,会出现写回主内存覆盖问题

 

l 解决volatile不保证原子性问题

1)方法前加synchronized解决

2)加锁解决

// 使用锁保证数据原子性

Lock lock = new ReentrantLock();

3)原子类解决

// 因为volatile不保证原子性,所以选择原子类AtomicInteger来解决volatile不保证原子性问题

private AtomicInteger atomicNumber = new AtomicInteger();

 

9.10什么是线程安全

1) 一个进程有多个线程在执行,而多个线程又可能会同时运行这段代码,如果每次运行结果和单线程运行结果是一样的,其他变量的值和预期也是一样的,则是线程安全。

2) vector的所有方法都是用同步实现的,因此是线程安全

补充:如何保证线程安全

· 方法一:使用安全类,比如 Java. util. concurrent 下的类。

· 方法二:使用自动锁 synchronized.

方法三:使用手动锁 Lock。

手动锁 Java 示例代码如下:

Lock lock = new ReentrantLock();

lock. lock();

try {

System. out. println("获得锁");

} catch (Exception e) {

// TODO: handle exception

} finally {

System. out. println("释放锁");

lock. unlock();

}

 

9.11java的竞争条件

1) 多线程对一些资源的竞争就会产生竞争条件,如果执行的程序竞争失败,则会导致排到后面执行,那么程序就会出现一些bugs。例如无序处理

 

9.12java如何停止一个线程

1) 没有提供比较合适的API,都有潜在的死锁威胁。

2) 本身run或者call执行完成后,就会自动结束线程。

3) 如果要手动结束,可通过volatitle变量、布尔变量来跳出run或者call的循环执行或者取消任务来中断线程

 

9.13线程异常会怎么样

1) 如果抛出异常,则线程会直接结束。

2) 可用uncatchaexceptionHandler接口来捕获异常并处理

 

9.14线程如何共享数据

1) 通过共享对象来实现目的。

2) 使用阻塞队列并发的数据结构

 

9.15java的notify和notifyAll

1) notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制

2) notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争

 

9.16notify、notifyAll为什么不在Thread类中

wait、notify、notifyAll都是锁级别的操作,所以把他们放在Object类中,因为锁属于对象

 

9.17什么是ThreadLocal变量

1) 是java中一种特殊的变量,所有线程都拥有一个ThreadLocal变量的副本,没有竞争条件了。

2) 可以使SimpleDateFormatter变成线程安全

 

9.18什么是FutureTask

1) java并发编程中FutureTask表示一个可以取消的异步运算,它有启动和取消运算、查询运算、返回运算结果等方法。

2) FutureTask对象可以调用Runable、Callable对象进行封装。

3) 由于FutureTask对象调用了Runable接口,因此可以提交给Excetors执行

 

9.19interrupted、isInterrupted

1) interrupted用于中断线程,会清空中断标识

2) isInterrupted用于查询中断标识

 

9.20wait、notify为什么一定在同步块中调用

1) api强制要求的。

2) 避免wait和notify产生竞争条件

 

9.21java的同步集合和并发集合

1) 同步和并发集合都是为多线程和并发提供线程安全的集合,不过并发集合扩展性高。

2) 同步集合在多线程并发的时候会导致争用,阻碍了系统的扩展性。

3) 并发集合例如ConcurrentHashMap,使用锁分离和内部分区技术提高了扩展性

 

9.22什么是线程池,为什么使用

创建线程要花费昂贵的时间和资源,如果任务来了才去创建线程那么响应时间就会变长,并且一个进程创建的线程数量有限,因为为了解决这些问题,在程序启动时就创建若干线程来处理响应,他们就被称为线程池,里面线程叫做工作线程

 

9.23多线程如何实现生产者和消费者问题

1) 比较低级的是用wait和notify。

2) 比较好的是BlockingQueue

 

9.24如何检测线程是否包含锁

Thread有一个holdsLock()方法可以查看当前线程是否拥有某个对象锁

 

9.25synchronized和ReentrantLock区别

synchronized不能扩展锁之外的方法和块边界,并且获取锁不能中途取消

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值