进程
进程是正在运行的程序
- 是系统进行资源分配和调用的独立单位
- 每一个进程都有自己的内存空间和系统资源
线程
是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则成为单线程程序
- 多线程:一个进程如果只有多条执行路径,则成为多线程程序
线程实现方式
- 方式一:继承Thread类
- 定义一个类继承Thread类,并重写run()方法,通过创建该类对象启动线程。
- 启动线程时调用start()方法而不是run()方法
run() :封装线程执行的代码,直接调用,相当于普通方法的调用
start() :启动线程;然后由JVM调用此线程的run()方法
- 方式二:实现Runnable接口
- 定义一个类实现Runnable接口,并在类中重写run()方法,最后创建该类对象,将其作为Thread类的对象的参数
- 相比继承Thread类,实现Runnable接口的好处:
1)避免了Java单继承的局限性
2)适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的思想。
线程调度
- 分时调度模型:轮流使用CPU,平均分配每个线程占用CPU的时间。
- 抢占式调度模型:优先级高的线程优先使用CPU,获取时间片也相对多一些。Java使用抢占式调度模型。
- 通过setPriority()方法来设置优先级,优先级最高为10,最低为1,默认为5。优先级高只是表示该线程获得CPU使用权的几率高,并不是要先把优先级高的执行完。
线程控制
- static void sleep(long millis):使进程停留millis毫秒
- void join() :等待该进程死亡
- void setDaemon(boolean on) :设置守护进程,当运行的进程都是守护线程时,JVM将会退出。
线程生命周期
线程同步
解决多线程安全问题。
- 判断是否出现安全问题的标准:
- 是否是多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
- 同步代码块
同步代码块可以理解为一个锁,当有线程进入代码块后就上了锁,只能等待代码执行完,锁才会释放。
synchronized(Object) {//Object代表任意对象
//多条语句操作共享数据的代码
}
同步代码块的好处和弊端:
好处:解决了多线程的数据安全问题
弊端:耗费资源,降低运行效率
- 同步方法
- 同步方法就是把synchronized关键字加到方法上
//修饰符 synchronized 返回值类型 方法名(方法参数) { }
- 同步方法的锁对象是this
- 同步静态方法:把synchronized关键字加到静态方法上
//修饰符 static synchronized 返回值类型 方法名(方法参数) { }
4)同步静态方法的锁对象:类名.class
- 线程安全的类
- StringBuffer,用法与StringBuilder一致,不考虑线程安全的情况下推荐使用StringBuilder ,因为StringBuilder不执行同步。
- Vector,用法与ArrayList一致,不考虑线程安全的情况下推荐使用ArrayList ,因为ArrayList不执行同步。
- Hashtable,用法与HashMap一致,不考虑线程安全的情况下推荐使用HashMap,因为HashMap不执行同步。
- 实际应用中,考虑线程安全情况下,一般StringBuffer可以使用,Vector和Hashtable也不使用,而是使用synchronizedList() 和 synchronizedMap()方法
- Lock锁
- 锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读取锁。
- Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。Lock是一个接口,可通过ReentrantLock类实例化,lock锁对象通过lock()方法获得锁,unlock()方法释放锁。
生产者消费者
- 一种多线程协作模式。生产者生成共享数据,消费者消费共享数据。