Java知识总结--线程篇

线程

文章部分内容来自于https://blog.csdn.net/zyx1260168395/article/details/105088359线程篇内容

文章部分内容来自于https://blog.csdn.net/qq_41701956/article/details/103253168


目录

线程

1.1- 创建线程有的常见方式方法

1.2-Runnable和Callable的区别

1.3- start()和run()方法的区别

1.4- sleep() 和 wait()的区别

1.5- notify()和 notifyAll()的区别

1.6-线程池有哪些参数

1.7- 线程池有几种(5种)?拒绝策略有几种(4种)?阻塞队列有几种(3种)?

1.8- 如何检测死锁?

1.9- 防止死锁

1.10- volatile底层是怎么实现的?

1.11- volatile与synchronized有什么区别?

1.12- java中的线程有几种状态(5种)

1.13-线程池有几种状态(5种)

1.14-在具体多线程编程实践中,如何选用Runnable还是Thread?

更多多线程常见面试题及答案



1.1- 创建线程有的常见方式方法

  • 通过继承Thread类实现一个线程

  • 通过实现Runnable接口实现一个线程

  • 实现Callable接口,允许有返回值

  • 使用线程池


1.2-Runnable和Callable的区别

最大的不同点:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;


Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

  • Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞!


1.3- start()和run()方法的区别

  • start()方法来启动线程,而启动后的线程运行的是run()方法中的代码。

  • run()方法当作普通方法的方式调用时,并不会开启新的线程。


1.4- sleep() 和 wait()的区别

  • sleep():方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。

  • wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。


1.5- notify()和 notifyAll()的区别

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。

  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。


1.6-线程池有哪些参数

最常用的三个参数:

  • corePoolSize:核心线程数
  • queueCapacity:任务队列容量(阻塞队列)
  • maxPoolSize:最大线程数

三个参数的作用:

  • 当线程数小于核心线程数时,创建线程。
  • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  • 当线程数大于等于核心线程数,且任务队列已满
    • 若线程数小于最大线程数,创建线程
    • 若线程数等于最大线程数,抛出异常,拒绝任务

1.7- 线程池有几种(5种)?拒绝策略有几种(4种)?阻塞队列有几种(3种)?

  • 五种线程池:

    ExecutorService threadPool = null;
    threadPool = Executors.newCachedThreadPool();//有缓冲的线程池,线程数 JVM 控制
    threadPool = Executors.newFixedThreadPool(3);//固定大小的线程池
    threadPool = Executors.newScheduledThreadPool(2);//一个能实现定时、周期性任务的线程池
    threadPool = Executors.newSingleThreadExecutor();//单线程的线程池,只有一个线程在工作
    threadPool = new ThreadPoolExecutor();//默认线程池,可控制参数比较多   
    
  • 四种拒绝策略:
RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务
  •  三种阻塞队列:

    BlockingQueue<Runnable> workQueue = null;
    workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列,有界
    workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列,无界
    workQueue = new SynchronousQueue<>();//无缓冲的等待队列,无界
    

1.8- 如何检测死锁?

利用Java自带工具JConsole
Java线程死锁查看分析方法


1.9- 防止死锁

死锁的四个必要条件:

  • 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
  • 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
  • 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
  • 环路等待条件:是指进程发生死锁后,若干进程之间形成一种头尾相接的循环等待资源关系

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之 一不满足,就不会发生死锁。

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和 解除死锁。

所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确 定资源的合理分配算法,避免进程永久占据系统资源。

此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。


1.10- volatile底层是怎么实现的?

当一个变量定义为volatile后,它将具备两种特性:1. 可见性,2. 禁止指令重排序。

可见性:编译器为了加快程序运行速度,对一些变量的写操作会现在寄存器或CPU缓存上进行,最后写入内存。而在这个过程中,变量的新值对其它线程是不可见的。当对volatile标记的变量进行修改时,先当前处理器缓存行的数据写回到系统内存,然后这个写回内存的操作会使其他CPU里缓存了该内存地址的数据无效。

处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致。如果一个正在共享的状态的地址被嗅探到其他处理器打算写内存地址,那么正在嗅探的处理器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充。 


1.11- volatile与synchronized有什么区别?

  • volatile仅能使用在变量上,synchronized则可以使用在方法、类、同步代码块等等。

  • volatile只能保证可见性和有序性,不能保证原子性。而synchronized都可以保证。

  • volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.


1.12- java中的线程有几种状态(5种)

  • NEW:新建状态,创建了一个线程对象

  • RUNNABLE:可以运行状态,线程调用了start()方法。

  • BLOCKED:阻塞状态,遇到了阻塞事件,或者等待锁对象.

  • WAITING:等待状态。wait(),join()/TIMED_WAITING:等待状态,sleep()

  • TERMINATED:终止状态


1.13-线程池有几种状态5种

Running、ShutDown、Stop、Tidying、Terminated。


1.14-在具体多线程编程实践中,如何选用Runnable还是Thread?

 Java中实现多线程有两种方法:继承Thread类、实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势:

    1、可以避免由于Java的单继承特性而带来的局限;

    2、增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;


更多多线程常见面试题及答案

//持续更新。。。。。。

猜你喜欢

转载自blog.csdn.net/weixin_44519467/article/details/106747812