java并发面试汇总

1. 线程的安全性问题:

线程安全和非线程安全: 一个类在单线程环境下能够正常运行,并且在多线程环境下,使用方不做特别处理也能运行正常,我们就称其实线程安全的。反之,一个类在单线程环境下运行正常,而在多线程环境下无法正常运行,这个类就是非线程安全的。

线程安全问题体现在:

  1. 原子性:原子,即一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
  2. 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。(synchronized,volatile)
  3. 有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
  • 出现线程安全问题的原因:
    线程切换带来的原子性问题
    缓存导致的可见性问题
    编译优化带来的有序性问题
2. 并行和并发有什么区别?

并发:多个任务在同一个 CPU 核上,按时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
并行:单位时间内,多个处理器同时处理多个任务,是真正意义上的“同时进行”。
串行:有n个任务,由一个线程按顺序执行。由于任务、方法都在一个线程执行所以不存在线程不安全情况,也就不存在临界区的问题。
做一个形象的比喻:
并发 = 两个队列和一台咖啡机。
并行 = 两个队列和两台咖啡机。
串行 = 一个队列和一台咖啡机。

3. 多线程

多线程:宏观上看,一个程序中可以同时运行多个不同的线程来执行不同的任务。

多线程的好处:
可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
坏处:
并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、死锁还有受限于硬件和软件的资源闲置问题。

4. 进程和线程的区别

这里我以java为例讲进程与线程的区别:

  • 首先,进程是一个程序运行的实例,是一个资源分配的基本单位,而线程是程序独立运行的最小单位
  • 从资源方面看,进程有自己独立的寻址空间,而且一个进程可以有多个线程,多个线程共享进程的堆和方法区(JDK1.8后的元空间),但是每个线程也有自己的程序计数器、虚拟机栈、本地方法栈
  • 从安全性和健壮性方面比较,进程间是相互独立的,一个进程死掉不会影响其他的进程,而在多线程环境下,线程间极有可能相互影响,可能会存在死锁,线程不安全的情况。
  • 从系统开销方面看,由于线程资源共享,使用相同的地址空间,因此线程切换的系统开销小,但不利于资源的管理和保护,进程则相反。
  • 在通信方面,线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。
5. Linux 上查找哪个线程cpu利用率最高?

windows上面用任务管理器看,linux下可以用 top 这个工具看。

  1. 找出cpu耗用厉害的进程pid, 终端执行top命令,然后按下shift+p 查找出cpu利用最厉害的pid号
  2. 根据上面第一步拿到的pid号,top -H -p pid 。然后按下shift+p,查找出cpu利用率最厉害的线程号,比如top -H -p 1328
  3. 将获取到的线程号转换成16进制,去百度转换一下就行
  4. 使用jstack工具将进程信息打印输出,jstack pid号 > /tmp/t.dat,比如jstack 31365 > /tmp/t.dat
    编辑/tmp/t.dat文件,查找线程号对应的信息
6. 线程死锁

死锁:指两个或两个以上的进程(线程)由于竞争资源而造成的一种阻塞的现象,若无外力作用,将永远在互相等待,互相僵持下去。
比如说,两个线程互相持有对方资源,同时他们都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

  • 形成死锁的四个必要条件是什么
    互斥条件:线程对于所分配到的资源具有排它性,即一个资源只能被一个线程占用,直到被该线程释放
    请求与保持条件:一个线程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
    不剥夺条件:已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
    循环等待条件:当发生死锁时,所等待的线程必定会形成一个环路(类似于死循环),造成永久阻塞
  • 如何避免线程死锁
    我们只要破坏产生死锁的四个条件中的其中一个就可以了。
  1. 破坏互斥条件
    这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的

  2. 破坏请求与保持条件
    一次性申请所有的资源。

  3. 破坏不可剥夺条件
    占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

  4. 破坏循环等待条件
    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。

7. 创建线程有哪几种方式?
  • 创建线程有四种方式:
    继承 Thread 类;
    实现 Runnable 接口;
    实现 Callable 接口;
    使用 Executors 工具类创建线程池

1) 继承 Thread 类

  1. 定义一个Thread类的子类,重写run方法,将相关逻辑实现,run()是线程要执行的业务逻辑方法
  2. 创建自定义的线程子类对象
  3. 调用子类实例的star()方法来启动线程

2 )实现 Runnable 接口

  1. 定义Runnable接口实现类MyRunnable,并重写run()方法
  2. 创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象
  3. 调用线程对象的start()方法

3)实现 Callable 接口
4. 创建实现Callable接口的类myCallable
5. 以myCallable为参数创建FutureTask对象
6. 将FutureTask作为参数创建Thread对象
7

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值