线程与进程
进程
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少 有一个线程
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分 成若干个线程
同步,异步&并发,并行
同步:排队执行,效率低但安全
异步:同时执行,效率高但不安全
并发:指两个或多个事件在同一段事件内发生
并行: 指两个或多个事件在同一时刻内发生(同时发生)
继承Thread
实现Runnable
线程休眠sleep
使线程休眠,会将运行中的线程进入阻塞状态。当休眠时间结束后,重新争抢cpu的时间片继续运行
// 方法的定义 native方法
public static native void sleep(long millis) throws InterruptedException;
try {
// 休眠2秒
// 该方法会抛出 InterruptedException异常 即休眠过程中可被中断,被中断后抛出异常
Thread.sleep(2000);
} catch (InterruptedException异常 e) {
}
try {
// 使用TimeUnit的api可替代 Thread.sleep
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
线程阻塞
线程的阻塞可以分为好多种,从操作系统层面和java层面阻塞的定义可能不同,但是广义上使得线程阻塞的方式有下面几种
BIO阻塞,即使用了阻塞式的io流
sleep(long time) 让线程休眠进入阻塞状态
a.join() 调用该方法的线程进入阻塞,等待a线程执行完恢复运行
sychronized或ReentrantLock 造成线程未获得锁进入阻塞状态 (同步锁章节细说)
获得锁之后调用wait()方法 也会让线程进入阻塞状态 (同步锁章节细说)
LockSupport.park() 让线程进入阻塞状态 (同步锁章节细说)
线程的中断
打断标记:线程是否被打断,true表示被打断了,false表示没有
isInterrupted() 获取线程的打断标记 ,调用后不会修改线程的打断标记
interrupt()方法用于中断线程
可以打断sleep,wait,join等显式的抛出InterruptedException方法的线程,但是打断后,线程的打断标记还是false
打断正常线程 ,线程不会真正被中断,但是线程的打断标记为true
守护用户的线程,当最后一个用户线程结束时,所有守护线程自动死亡。
线程不安全
解决方案1.同步代码块
格式:synchronized(锁对象){
}
解决方案2.同步方法
同步代码块和同步方法 都属于隐式锁
解决方案 3.显式锁 Lock和ReentranLock
三、等待是否可中断
Sync是不可中断的,除非抛出异常或者正常运行完成。
Lock可以中断的。
中断方式:
1、调用设置超时方法tryLock(long timeout ,timeUnit unit)
2、调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
四、加锁的时候是否可以公平
Sync:非公平锁。即排队抢锁。
Lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。
true:公平锁。即Lock lo = new ReentrantLock(true)
false:非公平锁
五、锁绑定多个条件来condition
Sync:要么随机唤醒一个线程,要么是唤醒所有等待的线程。
Lock:用来实现分组唤醒需要唤醒的线程,可以精确的唤醒,而不是像sync那样,不能精确唤醒线程。
六、性能比较
Sync是托管给JVM执行的。在JDK5中,前者因为需要调用接口操作,可能加锁等消耗时间更长。但在JDK6后,sync进行很多优化,有适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等,性能也不比lock差。
Lock是java写的控制锁的代码。对象性能更高点。
之前的sync使用悲观锁的机制,即线程独占锁,其它线程只能依靠阻塞来等待线程释放的锁,而线程阻塞时会引起线程的上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
而Lock使用乐观锁的方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁的实现方式是CAS机制(Compare And Swap),调用的是CPU提供的底层指令。