Java——多线程(校招准备)

线程创建方式

  • 继承Thread类
  • 实现Runnable接口

线程的基本状态以及状态之间的关系?

  • 创建:创建进程拥有PCB,但是其它资源还未就绪
  • 就绪(runable):其他资源都准备好,就差cpu。线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。
  • 执行(Running):也获得了cpu
  • 阻塞(Block):其它设备没有就绪从而放弃CPU的使用权
  • 终止:归还pcb
    在这里插入图片描述

线程的sleep()方法和yield()方法有什么区别

①sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。


请说明一下sleep() 和 wait() 有什么区别?

  • sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
  • wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

请分析一下同步方法和同步代码块的区别是什么?


线程的基本状态以及状态之间的关系?

  • 同步方法默认用this或者当前类class对象作为锁;
  • 同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法。

join()的作用

让“主线程”等待“子线程”结束之后才能继续运行。

在线程A中 调用 线程B对象.join( )方法:

  1. 如果线程B 是可运行态,则线程A阻塞自己运行,直到线程B 运行结束;
  2. 如果线程B 不是可运行状态,则继续执行线程A,并不会执行线程B.。

synchronized 关键字和 volatile 关键字的区别

  • volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场景还是更多一些。
  • 多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
  • volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。

Java的原子性&&可见性&&有序性

  • 原子性:是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。
    • synchronized可以保证原子性
    • x=10是原子性
    • i++不是原子性
    • y=x不是原子性
  • 可见性:当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。
    • volatile可以保证
    • synchronized可以保证
    • Lock
  • 有序性:即程序执行的顺序按照代码的先后顺序执行。
    • volatile
    • synchronized
    • Lock

执行execute()方法和submit()方法的区别是什么呢?

  • execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  • submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值。

为什么要用线程池?

  • 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

如何创建线程池

  • FixedThreadPool :
public class ThreadPoolDemo {
	public static void main(String[] args) {
		//创建线程池对象
		ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
		//创建Runnable实例对象
		MyRunnable r = new MyRunnable();
		
		//自己创建线程对象的方式
		//Thread t = new Thread(r);
		//t.start(); ---> 调用MyRunnable中的run()
		
		//从线程池中获取线程对象,然后调用MyRunnable中的run()
		service.submit(r);
		//再获取个线程对象,调用MyRunnable中的run()
		service.submit(r);
		service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

//关闭线程池
		//service.shutdown();
	}
}

该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

  • CachedThreadPool:
    该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。

线程实现callable接口和runnable接口的区别

  • 返回值:Callable有返回值(call()方法),Runnable没有返回值
  • 异常:
    • Runnable没有容错机制,意味着如果出现异常必须立即处理;
    • Callable有容错机制,意味着出现异常之后可以向上抛出
  • 启动方式:
    • Runnable可以通过Thread来启动,也可以通过线程池的execute、submit来处理;
    • Callable线程只能通过线程池的submit来处理`

notify()和 notifyAll()有什么区别?


Java中锁的分类

一、java中锁的分类
1、可重入锁

  • 锁的重入性:如果某个线程试图获得一个由它自己持有的锁,如果这个请求成功,那么这个锁具有重入性(内置锁具有重入性);如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

2、可中断锁

  • 就是可以相应中断的锁。在Java中,synchronized就不是可中断锁,而Lock是可中断锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

3、公平锁(一般情况下非公平锁性能好一些)

  • 公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

4、读写锁

  • 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。  
    ReadWriteLock就是读写锁,它是一个接口,
    ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。

volatile关键字

保证多线程并发执行时某个线程对数据的修改对其他线程的可见性

原理:

  • 使用volatile的时候,数据将会直接被写入主内存,而不会缓存在工作内存,因此针对volatile的关键字修饰的变量的操作,所有线程都是可以看见的

应用场景:

  • 写入变量值不依赖当前变量的当前值时
  • 该变量没有包含在具有其他变量的不变式中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值