Java多线程、死锁、创建线程Thread、Runnable、Callable、Executor创建线程池、生命周期、为什么一定调用start方法、synchronized原理、ThreadLocal

Java多线程

并行和并发有什么区别?

并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群。

所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

进程和线程的区别是什么?

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见

线程上下文切换比进程上下文切换要快得多

在多线程OS中,进程不是一个可执行的实体

守护线程是什么?

守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。

创建线程有几种不同的方式?你喜欢哪一种?为什么?

继承Thread类

实现Runnable接口

实现Callable接口

使用Executor框架来创建线程池

实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用

线程的生命周期有哪几种状态?它们之间如何流转的?

NEW:毫无疑问表示的是刚创建的线程,还没有开始启动。

RUNNABLE: 表示线程已经触发 start方式调用,线程正式启动,线程处于运行中 状态。

BLOCKED:表示线程阻塞,等待获取锁,如碰到 synchronized、lock 等关键字等占用临界区的情况,一旦获取到锁就进行 RUNNABLE 状态继续运行。

WAITING:表示线程处于无限制等待状态,等待一个特殊的事件来重新唤醒,如 通过wait方法进行等待的线程等待一个 notify或者 notifyAll方法,通过 join方 法进行等待的线程等待目标线程运行结束而唤醒,一旦通过相关事件唤醒线程,线 程就进入了 RUNNABLE 状态继续运行。

TIMED_WAITING:表示线程进入了一个有时限的等待,如 sleep(3000),等待 3 秒 后线程重新进行 RUNNABLE 状态继续运行。

TERMINATED:表示线程执行完毕后,进行终止状态。需要注意的是,一旦线程通过start 方法启动后就再也不能回到初始 NEW 状态,线程终止后也不能再回到 RUNNABLE 状态 。

为什么要创建线程池

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁

创建线程池的方式?

1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。 
2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是: 
1).工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。 
2).如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。 
3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。 
4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。(这种线程池原理暂还没完全了解透彻) 

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

类的不同:sleep() 来自 Thread,wait() 来自 Object。

释放锁:sleep() 不释放锁;wait() 释放锁。

用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。

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

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

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

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

只有调用了 start方法,才会表现出多线程的特性,不同线程的 run方法里面的代码交替执行。如果只是调用 run方法,那么代码还是同步执行的,必须等待一个线程的 run方法里面的代码全部执行完毕之后,另外一个线程才可以执行其 run 方法里面的代码。

同步方法和同步代码块的区别是什么?

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

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

execute():只能执行 Runnable 类型的任务。

submit():可以执行 Runnable 和 Callable 类型的任务。

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

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

方法二:使用自动锁 synchronized。

方法三:使用手动锁 Lock。

什么是死锁?

当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。

死锁发生的个必要条件

互斥条件:同一时间只能有一个线程获取资源。

不可剥夺条件:一个线程已经占有的资源,在释放之前不会被其它线程抢占

请求和保持条件:线程等待过程中不会释放已占有的资源

循环等待条件:多个线程互相等待对方释放资源

预防死锁(破坏这四个必要条件)

·由于资源互斥是资源使用的固有特性,无法改变,我们不讨论

·破坏不可剥夺条件

一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式地释放重新加入到系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行

·破坏请求与保持条件

第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源,

第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源

·破坏循环等待条件

采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少地采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程

怎么防止死锁?

尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。

尽量使用 Java. util. concurrent 并发类代替自己手写锁。

尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。

尽量减少同步的代码块。

Thread Local 是什么?有哪些使用场景?

ThreadLocal 是 JDK java.lang 包下的一个类,是天然的线程安全的类,用在多线程的场景!

用途:

保存线程上下文信息,在任意需要的地方可以获取!: 比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。

还有比如Spring的事务管理,用ThreadLocal存储Connection,从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。

线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!: 由于不需要共享信息,自然就不存在竞争问题了,从而保证了某些情况下线程的安全。

线程间数据隔离

经典使用场景数据库连接和 session 管理等。

典型使用场景2: 每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。

class User {

    String name;

    public User(String name) {

        this.name = name;

    }

}

class UserContextHolder {

    public static ThreadLocal<User> holder = new ThreadLocal<>();

}

UserContextHolder.holder.set(user);

User user = UserContextHolder.holder.get();

Java中的volatile 变量是什么?

volatile是一个特殊的修饰符,只有成员变量才能使用它。保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

synchronized 和 volatile 的区别是什么?

volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。

volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。

volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。

synchronized 和 Lock 有什么区别?

synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。JDK1.5以后引入了自旋锁、锁粗化、轻量级锁,偏向锁来有优化关键字的性能。

Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了

synchronized 实现原理和应用总结?

Synchronized保证同一时刻只有一个线程可以进入临界区

Synchronized是对对象进行加锁,在Jvm中,对象在内存中分为三块区域:对象头、实例数据和对齐填充。

对象头中保存了锁标志位和指向Monitor对象的起始地址。当Monitor被某个线程持有后,就会处于锁定状态,Owner部分会指向持有Monitor对象的线程。另外Monitor中还有两个队列,用来存放进入及等待获取锁的线程。

Synchronized应用在方法上时,在字节码中是通过方法的AccCC_Synchronized标志来实现的,Synchronized应用在同步块上时,在字节码中是通过Monitorenter和Monitorexit实现的。

针对Synchronized获取锁的方式,Jvm使用了锁升级的优化方式,就是先使用偏向锁优先同一线程再次获取锁,如果失败,就升级为Cas轻量级锁,如果再失败会短暂自旋,防止线程被系统挂起。最后如果以上都失败就是升级为重量级锁

说一下 atomic 的原理?

atomic 主要利用 CAS (Compare And Swap) 和 volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

解决线程同步与互斥的主要方式是Cas、Synchronized、和Lock。

CAS(Compare and Swap),即比较并替换,乐观锁的一种实现,实现并发算法时常用到的一种技术,实现多线程执行的安全性。

在CAS中,比较和替换是一组原子操作,不会被外部打断

CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。

CAS容易出现Aba问题,无法判断是否发生过修改。不一定会影响结果,但还是需要防范,解决的办法可以增加版本号控制或者时间戳。Juc工具包中提供了这样的类。

AQS

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值