什么是Java中的线程同步?
回答:我理解的线程同步就是:在程序中会有多个线程对资源进行修改或者占有,我们需要一种机制来进行线程的持有资源的顺序来保证数据的一致性和完整性;在Java中来实现线程的同步有syn和lock;来保证在同一时刻只有一个线程能够访问共享资源
Java的线程安全是什么?
回答:多个线程对同一个资源进行访问,不会出现数据不一致和不正确性的问题;
常用的线程安全措施有:使用锁机制 如syn、lock ;原子类、线程安全容器;
Java中synchronized是怎么实现的?
回答:syn的实现原理是基于jvm中的monitor和对象头来实现;当syn修饰方法时该方法会有一个标志,表示该方法为同步方法,需要获取监视器锁才能执行该方法;当syn修饰代码块的时候,在编译的时候会有两个指令,分别时monitorenter和monitorexit 进入代码块需要获取monitor才能执行
为什么要使用并发编程?
提高多核CUP的利用率;同时利于业务的拆分,提高整体的效率和性能
使用并发编程有什么缺点?
使用并发编程在提高性能的情况下,同时也会带来很多问题:例如内存泄漏、上下文切换带来的资源的损耗、多个线程竞争资源带来的死锁问题等等
并发编程的三个必要的要素是什么?
首先是原子性:就是一个操作是不可再分的;就是一个或多个操作要么全部成功要么全部失败
可见性:就是一个线程对于公共资源的修改,另一个线程是可见的;
有序性:计算机在执行指令的时候能够按照代码的顺序进行执行(处理器可能会对指令进行优化)
在Java中如何保证多线程的安全?
对于原子性来说,Java中利用锁来保证,例如synchronized 或者lock
对于可见性来说,Java可利用锁和voltile 来保证可见性;
对于有序性来说,Java是利用Happend-before的原则来保证有序性问题(voltile 可以理解为 Happend-before原则的一个实现)
并行和并发的区别?
并行可以理解为:多个处理器同时处理多个任务;同一时间
并发可以理解为:一个处理器根据时间片处理多个任务。同一时间
进程和线程的区别?
进程可以理解为一个应用程序,它是操作系统分配资源的基本单位;线程的话是cup调度的基本单位,一个进程可以有一个或多个线程;
什么是上下文切换?
上下文切换可以理解为,cpu在处理多个任务的时候是根据时间片轮转法来进行任务的处理,当一个任务的时间片到了的时候,此时会记录下该任务处理到那个位置,即保存当前任务的状态,当cpu在轮转到该任务的时候可以从上一次的状态进行处理;任务的保存到再加载的过程就是一次上下文切换;
守护线程和用户线程有什么区别?
用户线程可以理解为我们用户运行的具体任务,如程序的主线程;守护线程是为用户线程服务的线程,例如垃圾回收线程
什么是线程死锁?
线程死锁就是两个或多个线程为了争取相关资源,再无外力的作用下陷入的一种阻塞状态(由于彼此通信)一个很简单的例子就是 线程A需要线程B 的资源,而线程B需要线程A 的资源,所以两者相互等待,进入死锁状态
形成死锁的四个必要条件是什么?
第一个是互斥性,第二个是不可抢占性,第三个是占有且等待,第四个是循环等待;
第一个就是一个资源只能由一个线程占有,如果还有其他线程只能等待上一个线程释放资源才能够获取资源;
第二个,当线程申请的资源此时再别的线程拥有,那么该线程只能等待,不能够把别人的资源抢占过来;
第三个,是指一个线程已经拥有了一个资源的情况下,又申请新的资源,而其他资源又被其他线程占有,只能等待,但是对自己获取的资源保持不放;
第四个,由于第三个原因,导致申请资源的链路形成一个环,从而进入一种循环等待的状态;
如何避免死锁?
一、避免一个线程获取多个锁;二、避免一个线程在一个锁内拥有多个资源,尽量保证一个锁针对一个资源;三、尝试使用定时锁
介绍一下ThreadLocal
Thread中有两个变量是threadlocals和inheribthreadlocal,只有在threadlocalget或者set时才会初始化该值,而threadlocalget或者set其实调用了theadlocalmap get或set;threadlocalmap可以理解为为了threadlocal定制化的一个map,元素是存在map中;
用一个场景来描述threadlocal set或者get方法
在所有线程外创建了一个threadlocal 对象 记做 TL1, 首先在该线程中调用 TL1.get()中 得到的是该线程对象 记做t ,然后判断t中的threadlocals是否为空,如果为空就创建一个threadlocalmap,并且将(TL1,null)插入,最后get的方法返回的是get;如果不为空就返回具体的值;
线程之间如何通信及线程之间如何同步?
线程通信就是线程之间如何交换信息,一般线程之间有两种交互信息的方式,一种是共享内存,还有一种是消息传递;线程同步是指程序中用于控制不同线程之间操作发生的相对顺序的机制;
在Java中线程之间的通信是通过共享内存模式,Java线程之间的通信总是隐形的;在共享内存模型中,同步是显式进行的,程序员必须显式指定某个方法或者代码块需要在线程之间互斥执行
Java内存模型
JMM内存模型主要是定义了一个线程对另一个线程是可见的,即每一个线程都有自己的本地内存,共享变量放在主内存中,当一个线程A要和线程B 进行通信 必须要经历两个步骤,首先是线程A先把共享内存的值刷新到自己的本地内存,然后进行修改,然后将自己更新后的值刷新到共享内存中,线程B到主内存中去读取线程A之前已经更新的共享变量;
Java怎么进行并发控制
第一个:synchronized
用法:
synchronized修饰的方法或者代码块 同一时间只能 被一个线程所执行
修饰实例方法:调用某对象的方法需要获取该对象实例的锁
修饰静态方法:调用某方法前需要获取该类对象的锁
代码块:获取指定对象的锁;
两个线程分别执行同一个对象synchronized修饰的实例方法和静态方法时不会发生互斥;因为锁的资源不同,一个锁了对象实例,一个锁的是类对象;
有关Synchronized锁升级的知识:
原因:从jdk1.6后为了提高synchronized的效率,引入了偏向锁和轻量级锁,目的是为了减少频繁获取锁带来的上下文资源消耗以及提高
首先就是偏向锁,偏向锁适用于只有一个线程访问共享资源的情况下,此时使用偏向锁减少了加锁解锁带来的资源消耗,偏向锁会将此时的访问的线程id设置在锁对象的markword中,以便下次该线程来获取锁对象时就直接对比是否为对象头中的markword的线程id;
轻量级锁:适用的场景为短时间内多个线程争夺共享资源;具体实现:一个线程来争取锁,首先在该线程栈中会创建一个record,该record会复制锁对象头中的markword,然后会通过CAS操作来尝试获取锁,如果获取到了锁那么锁对象头中的markword变为指向record的指针,同时释放锁的时候会变为原来的markword
重量级锁,CAS操作失败后-轻量级锁升级为重量级锁时,那么就是使用到了操作系统中mutex互斥变量了,此时获取到了锁就继续执行,没有获取到锁就阻塞等待;
synchronized的底层原理:
是通过jvm中的monitor和对象头实现的,线程尝试获取对象的monitor,如果monitor已经被其他线程占有时,获取失败,该线程会进入entryset,占有monitor时调用wait 会进入WaitSet,调用notify时,会从waitset中随机获取一个线程进行唤醒,调用notifyall会唤醒waitset中的所有线程;
第二个:AQS
AQS可以理解为构建Java锁和同步器的一个框架,它起到了一个抽象、封装的作用;可以用于实现reentrantlocal等等;AQS主要由一个volatile类型的整数state和 一个双向链表 来实现同步控制;AQS实现同步的关键是有一个FIFO的队列,通过将等待线程加入等待队列中,然后再释放同步状态时候,从等待队列中唤醒等待线程,从而实现同步机制
AQS有两种方式:一种是独占式、一种是共享式;两种的区别在于是否可以多个线程共享资源;两者的实现方式也是差不多的,只是再释放和获取同步状态的方式不同
Synchronized关键字(重点)
synchronized关键字是Java用来控制并发控制的一种方式,解决了在多线程情况下资源共享问题;我们主要用synchronzed修饰方法或者代码块;两者底层实现有一些不同,修饰方法时是通过一个标志符来标志需要获取锁对象;代码块是通过monitorenter 和 monitorexit 来进行控制,当进入 monitorenter 需要获取锁,结束时释放锁;被synchronized修饰后,在任意时刻只能有一个线程执行;jdk1.6之后 优化了锁,带来了锁升级
Synchronized关键字和lock有什么区别?
首先Synchronized是java内置关键字,在jvm中lock是一个接口
Synchronized可以修饰类、方法、代码块;lock只能给代码块加锁
Synchronized不需要手动加锁解锁,lock需要手动加锁和解锁
lock更加灵活,可以知道有没有成功获取到锁