JAVA多线程与锁

35.并行和并发有什么区别?

并发的关键是你有处理多个任务的能力,但不是同时
并行的关键是你在同时处理多个任务。

36.线程和进程的区别?

1.线程(Thread)与进程(Process)

进程定义的是应用程序与应用程序之间的边界,一个进程就代表一个应用程序。不同的进程之间不能共享代码和数据空间,而同一进程的不同线程可以共享代码和数据空间。

2.一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程。
实现线程的两种方式:继承Thread类,实现Runable接口

37.守护线程是什么?

Java线程分为用户线程和守护线程。

  • 守护线程是程序运行的时候在后台提供一种通用服务的线程。所有用户线程停止,进程会停掉所有守护线程,退出程序。
  • Java中把线程设置为守护线程的方法:在 start 线程之前调用线程的 setDaemon(true) 方法。

注意:

  • setDaemon(true) 必须在 start() 之前设置,否则会抛出IllegalThreadStateException异常,该线程仍默认为用户线程,继续执行
  • 守护线程创建的线程也是守护线程
  • 守护线程不应该访问、写入持久化资源,如文件、数据库,因为它会在任何时间被停止,导致资源未释放、数据写入中断等问题

38.创建线程有哪几种方式?

第一种方式:继承Thread类
第二种方式:实现Runnable接口
第三种方式:使用内部类的方式
第四种方式:定时器
第五种方式:带返回值的线程实现方式
第六种方式:基于线程池的方式
Spring方式:使用Spring来实现多线程

39.说一下 runnable 和 callable 有什么区别?

相同点:
都是接口
都可以编写多线程程序
都采用Thread.start()启动线程


主要区别:
Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息


注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

40.线程有哪些状态?

线程状态有 5 种,新建,就绪,运行,阻塞,死亡

1. 线程 start 方法执行后,并不表示该线程运行了,而是进入就绪状态,意思是随时准备运行,但是真正何时运行,是由操作系统决定的,代码并不能控制,

2. 同样的,从运行状态的线程,也可能由于失去了 CPU 资源,回到就绪状态,也是由操作系统决定的。这一步中,也可以由程序主动失去 CPU 资源,只需调用 yield 方法。

3. 线程运行完毕,或者运行了一半异常了,或者主动调用线程的 stop 方法,那么就进入死亡。死亡的线程不可逆转。

4. 下面几个行为,会引起线程阻塞。

  • 主动调用 sleep 方法。时间到了会进入就绪状态
  • 主动调用 suspend 方法。主动调用 resume 方法,会进入就绪状态
  • 调用了阻塞式 IO 方法。调用完成后,会进入就绪状态。
  • 试图获取锁。成功的获取锁之后,会进入就绪状态。
  • 线程在等待某个通知。其它线程发出通知后,会进入就绪状态  

41.sleep() 和 wait() 有什么区别?

1、sleep()方法是线程类Thread的静态方法,让调用线程进入睡眠状态,让出执行机会给其它线程,等到休眠时间结束后,线程进入就绪状态和其它线程一起竞争CPU执行时间。

因为sleep()是静态的(static修饰的)方法,它不能改变对象的机锁,当一个synchronized块中调用了sleep方法,线程虽然进入休眠状态,但是对象的机锁没有释放,其它线程无法访问这个对象。

因为一个类中的静态资源在类class被java虚拟机加载时就已经被初始化了,而非静态资源要在对象实例化的时候才会被初始化。sleep()方法是静态的,只依赖于类,不依赖于对象,而锁是对象锁,所以不能改变锁。

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

wait()、notify()、notifyAll()只能在同步控制方法或者同步控制块里面使用,而sleep()可以在任何地方使用。

sleep()必须捕获异常,而wait()、notify()、notifyAll()不需要捕获异常。

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

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

有了这些理论基础,后面的notify可能会导致死锁,而notifyAll则不会的例子也就好解释了

43.线程的 run()和 start()有什么区别?

run()方法:

  是在主线程中执行方法,和调用普通方法一样;(按顺序执行,同步执行)

start()方法:

  是创建了新的线程,在新的线程中执行;(异步执行)

44.创建线程池有哪几种方式?

     newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程

    newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行

    newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定

顺序(FIFO,LIFO,优先级)执行

45.线程池都有哪些状态?

1.Running

状态说明:线程池处于Running状态时可以接受新的任务,以及对已添加的任务进行处理

状态切换:线程池的初始化状态是Running,换句话说,线程池一旦被创建,就处于Running状态,并且线程池中的任务数为0

2.SHUTDOWN

状态说明:此时线程池处于SHUtDOWN状态时,不在接受新任务,但能处理已添加的任务

状态切换:调用线程池SHUTDOWN()接口时,线程池由RUNNING->SHUTDOWN

3. STOP

状态说明:线程池处STOP的状态下,不再接受任务,吧并且会中断正在处理的任务

状态切换:调用线程池SHUTDOWNNow()接口时,线程池由Running或者SHUTDOWN->STOP

4 .TIDYING

状态说明:当所有的任务停止时,CTL记录的总数“任务量”为0,线程池就会变成TIDYING状态。当线程池变为TIDYING状态时,会执行子函数terminated(),terminated()在ThreadPoolExcutor类中为空,若是想在线程池变为TIDYING时,进行相应的处理,可以通过重载terminated()来实现

状态切换:当线程池哎SHUTDOWN状态下,阻塞队列为空并且线程池执行的任务也为空,就会由SHOWDOWN-》TIDYING当现在线程池为STOP状态下,线程池的执行为空时就会由STOP->TIDYING

4.TERMINATED

状态说明:线程池彻底终止,就变成了TERMINATED

状态切换:线程池处于TIDYING状态时,执行完terminated()以后就变成了TIDYING->TERMINATED

46.线程池中 submit()和 execute()方法有什么区别?

一:
submit()方法,可以提供Future < T > 类型的返回值。
executor()方法,无返回值。

execute无返回值

二:
excute方法会抛出异常。
sumbit方法不会抛出异常。除非你调用Future.get()。

三:
excute入参Runnable
submit入参可以为Callable,也可以为Runnable。

47.在 java 程序中怎么保证多线程的运行安全?

  1. 使用synchronied关键字,可以用于代码块,方法(静态方法,同步锁是当前字节码对象;实例方法,同步锁是实例对象)

  2. 使用volatile 关键字,防止指令重排,被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量

  3. lock锁机制

  4. 使用线程安全的类,比如Vector、HashTable、StringBuffer

48.多线程锁的升级原理是什么?

JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需要自动切换到合适的锁,这种切换就是锁的升级。升级是不可逆的,也就是说只能从低到高,也就是偏向-->轻量级-->重量级,不能够降级

  锁级别:无锁->偏向锁->轻量级锁->重量级锁

49.什么是死锁?

  • 死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,他们将无法推进下去;

50.怎么防止死锁?

产生死锁的必要条件:

1、互斥使用(资源独占)

  一个资源每次只能给一个进程使用(比如写操作)

2、占有且等待:

  进程在申请新的资源的同时,保持对原有资源的占有

3、不可抢占:

  资源申请者不能强行从资源占有者手动夺取资源,资源只能由占有者自愿释放

4、循环等待:

  A等待B占有的资源,B等待C占有的资源,C等待D占有的资源,..........N等待A的资源,形成一个线程等待回路

51.ThreadLocal 是什么?有哪些使用场景?

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
ThreadLocal在每个本地线程中创建了一个ThreadLocalMap对象,每个线程可以访问自己内部ThreadLocalMap对象里的value。通过这种方式,实现线程之间的数据隔离。

为每个线程分配一个JDBC连接的Connection。这样可以保证每个线程都在各自的Connection上进行数据库的操作,不会出现A线程关闭了线程B正在使用的Connection。

52.说一下 synchronized 底层实现原理?

53.synchronized 和 volatile 的区别是什么?

1、Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized、volatile、final、concurren包等。

2、synchronized通过加锁的方式,使得其在需要原子性、可见性和有序性这三种特性的时候都可以作为其中一种解决方案,看起来是“万能”的。的确,大部分并发控制操作都能使用synchronized来完成。

3、volatile通过在volatile变量的操作前后插入内存屏障的方式,保证了变量在并发场景下的可见性和有序性。

4、volatile关键字是无法保证原子性的,而synchronized通过monitorenter和monitorexit两个指令,可以保证被synchronized修饰的代码在同一时间只能被一个线程访问,即可保证不会出现CPU时间片在多个线程间切换,即可保证原子性。

那么,我们知道,synchronized和volatile两个关键字是Java并发编程中经常用到的两个关键字,而且,通过前面的回顾,我们知道synchronized可以保证并发编程中不会出现原子性、可见性和有序性问题,而volatile只能保证可见性和有序性。

54.synchronized 和 Lock 有什么区别?

synchronized和lock的区别:

类别synchronizedlock
存在层次java的关键字,在jvm层面上是一个类
锁的释放

1、以获取锁的线程执行完同步代码,释放锁

2、线程执行发生异常,jvm会让线程释放锁

在finally中必须释放锁,不然容易造成线程死锁
锁的获取

假设A线程获得锁,B线程等待,

如果A线程阻塞,B线程会一直等待

分情况而定,lock有多个锁获取的方法,可以尝试获得锁,

线程可以不用功一直等待

锁状态无法判断可以判断
锁类型可以重入,不可以中断,非公平可重入 可以判断 可公平
性能少量同步大量同步

55.synchronized 和 ReentrantLock 区别是什么?

  • synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
  • synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
  • synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
  • synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
  • synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
  • synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放
     

补充一个相同点:都可以做到同一线程,同一把锁,可重入代码块。

56.说一下 atomic 的原理?

大概实现原理是:通过CAS乐观锁保证原子性,通过自旋保证当次修改的最终修改成功,通过降低锁粒度(多段锁)增加并发性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值