进程(Process):
当一个程序进入内存运行时,即变成一个进程。
独立性:时系统中独立存在的实体。
动态性:程序是静态的,而每一个进程都拥有自己私有的地址空间。
并发性:多个进程可以在单个处理器上并发执行。
线程(Thread):
也被称为轻量级进程。多线程拓展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程是进程的执行单元。 一个进程可以拥有多个线程,一个线程必须拥有一个父进程。 线程可以拥有自己的堆栈、自己的程序计数器和局部变量,但不拥有系统资源。
多线程的优点:进程之间不能共享内存,但线程之间可以; 创建一个线程占用的系统资源少。
创建线程的三种方法:
1.继承Thread或其子类,重写run方法。 创建Thread子类实例就是创建了线程对象。
2.实现Runnable接口,重写run方法,该run方法同样是该线程的执行体,创建Runnable实现类的实例,并以此作为Thread的target来创建Thread对象。
3.使用Callable和Future创建线程,Callable接口提供了一个call方法,比run方法功能更强大:call()方法可以有返回值、可以声明抛出异常。FutureTask类实现了Futuer接口和Runnable接口,可以作为Thread类的target。
FutureTask task=new FutureTask<>((Callable) ()->{
int i=0;
for(;i<10;i++){
System.out.println(Thread.currentThread().getName()+”i的值:”+i);
}
return i;
});对比:
Runnable和Callable:
还可以及继承其他类;多个线程可以共享一个target对象实现多个线程来处理同一分资源的情况; 必须使用Thread.currentThread()访问当前线程。
Thread:
不能在继承其他类; 直接使用this可以访问当前线程。
currentThread()是Thread的静态方法,总是返回当前正在执行的线程对象; getName()是Thread类的实例方法,返回调用该方法的线程的名字。
thread中的实例变量并不会被共享,因为new 一个新的线程时便创建了一个新的实例。 而target中是同一个Runnable对象,所以便于共享。
线程的生命周期:
新建(New):
就绪(Runnable):
运行(Running):
阻塞(Blocked):
死亡(Dead):
启动线程如果直接调用对象run方法:run方法会被立即执行,run返回之前其它线程无法并发执行。相当于系统把线程对象当成了普通对象,run方法也变成了普通方法,而不是线程执行体。
start方法只能被调用一次,而且是用来调用处于新建状态的线程。否则会引发IllegalThreadStateException异常。当主线程结束时,其他线程并不受任何影响。一旦子线程启动起来后,就拥有和主线程相同的地位,不会受主线程的影响。
isAlive():判断线程是否已经死亡,新建(start之前)和死亡状态返回false。
stop方法可以用来结束线程,但容易导致死锁。
线程控制:
join():让一个线程等待另一个线程完成。 thread.join() 表示等thread线程执行完才会继续执行。
后台线程:其他线程提供服务的线程。例如JVM垃圾回收线程。 如果所有前台线程死亡,后台线程自动死亡。
sleep():让当前正在执行的线程暂停一段时间,但并不会释放对象锁。Thread类提供的静态方法,会使线程进入阻塞状态到时间后再进入就绪状态。
yield():线程让步,Thread类提供的静态方法,但不会阻塞该线程。转入就绪状态。此时优先级大于等于该线程的线程才会获得执行机会。(抢占式)
改变线程优先级:每个线程默认的优先级都与创建它的父线程优先级相同。 mian线程具有普通优先级 setPriority(1~10)。 getPriority().
线程同步:
同步代码块:run{synchronized(obj){ } } 写在run方法中。 加锁-》修改-》释放锁
同步方法:使用synchronized关键字修饰的方法。 在run方法中调用同步方法。 对于synchronized修饰的实例方法,无须显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的 对象。(StringBuilder单线程和StringBuffer多线程) 如果修饰静态方法,则相当于申请当前对象的 Class 锁
释放锁:wait(), Object方法,会释放同步监视器。
同步锁(Lock): lock使用显式Lock对象作为同步锁。 synchronized使用于竞争资源相关的、隐式的同步监视器,要求加锁和释放锁要出现在同一块结构中,当获得多个锁时,必须以相反顺序释放。 同样符合 加锁–修改–释放锁 的操作模式。(ReentrantLock可重入锁)
死锁:线程类的suspend() 挂起 方法容易导致死锁。 (resume)
sleep、yield、wait、suspend区别。
线程通信:
wait()、notify()、notifyAll() 属于Object类。必须由同步监视器对象(synchronized)调用。 对于同步方法,该类的默认实例this就是同步监视器;对于同步代码块,同步监视器是synchronized后括号里的对象,必须使用该对象调用这三个方法。
使用Condition:对于直接使用Lock对象保证同步,系统中不存在隐式的同步监视器,也就不能使用wait()、notify()、notifyAll()方法。 Condition实例被绑定在一个Lock对象锁上,调用Lock.newCondition()获得该实例。 Condition提供await() signal() signalAll()方法。
阻塞队列BlockingQueue: Queue子接口。当生产者线程试图向BlockingQueue放入元素时,如果队列已满,则该线程被阻塞;当消费者试图从BlockingQueue取元素时,如果队列已空,则该线程被阻塞。 两个线程交替放和取元素,从而控制线程通信。 put(E e) take()
线程池:
系统启动一个新线程的成本较高,因为涉及与操作系统的交互。 线程池在系统启动时即创建大量空闲线程,程序将一个Runnable对象或callable对象传给线程池,线程池会启动一个线程来执行run或call方法。当run或call方法执行结束后,该线程不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个run或call。