基本概念
- 程序:计算机语言编写的一组指令的集合。
- 进程:程序在运行内存中的产生、存在、消亡的过程。
- 线程:程序内部的执行路径。
- 多线程:同时并行执行多个线程;每个线程拥有独立的运行栈、程序计数器,共享堆、方法区。
java程序至少有三个线程:main()主线程、gc()垃圾回收进程,异常处理线程。
单核CPU与多核CPU的多线程
单核CPU多线程:断续执行多个线程(暂停一个线程,执行另一个线程),同一时间执行一个线程,因时间单元小,故宏观上表现为多线程,是一种假的多线程,实质为单线程,是一种并发。
多核CPU多线程:同定义的多线程,是一种并行,但每个核也可以执行假的多线程,即多核CPU具备并发和并行。
多线程优点
- 提高应用程序的相应,增强用户体验;
- 提高CPU的利用率,减少闲置;
- 改善程序结构,利于理解和修改。
多线程使用场景
- 程序需要同时执行多个任务;
- 存在等待任务时,如用户输入、文件读写操作、网络操作、搜索等;
- 需要后台运行程序。
多线程的创建和使用
一个线程对应一个线程类的一个对象,多个对象对应多个线程。
方式一
- 创建一个继承于Thread类的子类;
- 重写run方法:public void run() {线程执行的操作};
- 主类中创建线程对象;
- 执行该线程:对象名.start(); // 不能直接调用run方法,否则仍为main线程;同一个对象不能重复执行该线程,否则报异常。
获取当前当前线程的默认名:Thread.currentThread().getName()。//默认名为Thread-0, Thread-1, Thread-2…
注意:该方式创建的多线程,共享数据和同步监视器都必须声明为静态的,否则它们不唯一。
方式二
优先选择这种方式,它可以把类的对象和线程的对象分隔开,更能体现封装的特性。
- 创建一个实现了Runnable接口的类;
- 重写run方法:public void run() {线程执行的操作};
- 主类中创建该类的对象;
- 该对象作为有参构造器参数,创建Thread类对象thread;
- 执行该线程:thread.start();
Threa的常用方法
Thread类的静态方法
也可通过继承Thread类的子类直接调用
- Thread.currentThread() == 返回对当前正在执行的线程对象的引用
- Thread.sleep(毫秒数) == 使当前正在执行的线程以指定的毫秒数暂停
- Thread.yield() == 释放当前CPU的执行权,随机继续执行线程
Thread类的动态方法
也可通过继承Thread类的子类直接调用
- 创建对象:见多线程的创建
- 调用对象的方法
- thread.getName() == 返回此线程的名称
- thread.setName(线程名称) == 设置此线程的名称
- thread.isAlive() == 判断此线程是否存活,是则返回true
- thread.join() == 暂停当前线程并执行thread线程至thread线程消亡再继续执行当前线程
线程的优先级
线程的优先级是指线程优先执行的概率(0, 1)高低,并不是一定优先执行。
优先级常量
MAX_PRIORITY == 最大优先级:10
MIN_PRIORITY == 最小优先级:1
NORM_PRIORITY == 普通优先级(默认):5
获取和设置优先级
thread.getPriority() == 返回此线程的优先级
thread.setPriority() == 设置此线程的优先级
线程的生命周期
多线程的安全性
普通的多线程存在不安全问题:一个线程操作共享数据时出现阻塞,其它线程也参与进来操作共享数据,导致共享数据未及时改变出现重复,或导致操作完后共享数据越界。
解决原理:使操作共享数据的语句变成单线程的,即共享数据同步,只能同时被单个线程操作。
同步语句
synchronized(同步监视器){同步代码}
同步监视器:任何类的同一个对象,又称锁,同步语句执行时占用锁,执行完释放锁。可以考虑用this、类名.class替换。
同步方法
把同步代码提取出来,创建一个同步方法,然后调用该方法;这种同步方式给整个方法上锁,相对同步语句有一定局限性。
方式一的同步方法:private static synchronized void 方法名(){同步代码} // 必须声明为静态方法;隐含同步监视器当前类.class。
方式二的同步方法:private synchronized void 方法名(){同步代码} //隐含同步监视器this
Lock锁
JDK5新增,一种手动锁,即手动调用方法获得锁和释放锁,优先选择这种同步方式。
- 创建Lock对象(同步监视器):private static Lock lock = new ReentrantLock(); // 多态,方式一线程
private Lock lock = new ReentrantLock(); // 多态,方式二线程
- 创建Lock语句及调用方法:try{lock.lock(); 同步代码}finally{lock.unlock();}
死锁
多个线程执行同步代码时占用了对方的同步数据或同步监视器,一直进入阻塞状态。
避免死锁的办法:减少同步数据的定义;减少同步嵌套。