目录
线程是一个程序内部的一条执行流程
创建线程
继承Thread类
- 1、定义一个子类继承Thread类,成为一个线程类
- 2、重写Thread类的run方法
- 3、在run方法中编写线程的任务代码
- 4、创建线程类的对象,代表县城
- 5、调用start方法,启动线程
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展
注意:
- 启动线程必须是调用start方法,而不是run方法,直接调用run方法会被当成普通方法执行,此时相当于还是单线程执行
- 只有调用start方法才是启动一个新的线程执行
实现Runnable接口
- 1、定义一个线程任务类实现Runnable接口。
- 2、重写run方法,设置线程任务
- 3、创建MyRunnable任务对象
- 4、把MyRunnable任务对象交给Thread处理
- 5、调用线程对象的start()方法启动线程
优点:任务类只是实现接口,可以继续继承其他类、实现其他接口、扩展性强。
缺点:需要多一个Runnable对象
匿名内部类写法
- 1、可以创建Runnable的匿名内部类对象
- 2、再交给Thread线程对象
- 3、再调用线程对象的start()启动线程
实现Callable接口、FutureTask类实现
1、创建任务对象
- 定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据
- 把Callable类型的对象封装成FutureTask(线程任务对象)
2、把线程任务对象交给Thread对象
3、调用Thread对象的start方法启动线程
4、线程执行完毕后、通过FutureTask对象的get方法去获取线程任务执行的结果
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果
缺点:编码复杂一点
线程的常用方法
Thread的常用方法
public void run() | 线程的任务方法 |
public void start() | 启动线程 |
public String getName() | 获取当前线程的名称,线程名称默认是Thread-索引 |
public void setName(String name) | 为线程设置名称 |
public static Thread currentThread() | 获取当前执行的线程对象 |
public static void sleep(long time) | 让当前执行的线程休眠多少毫秒后,再继续执行 |
public final void join()... | 让调用当前这个方法的线程先执行完 |
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target,String name) | 封装Runnable对象成为线程对象,并指定线程名称 |
线程安全
多个线程,同时执行访问一个共享资源,存在修改该共享资源
线程同步
让多个线程先后依次访问共享资源,这样就可以避免出现线程安全问题
方案
加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能进来
同步代码块
作用:把访问共享资源的核心代码给上锁。
synchronized(同步锁){
访问共享资源的核心代码
}
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,
- 对于实例方案一般同步锁用this作为锁对象
- 对于静态方案建议使用字节码(类名.class)对象作为锁对象
同步方法
作用:把访问共享资源的核心方法给上锁。
修饰符 synchronized 返回值类型 方法名称(形成列表){
存在共享资源的代码
}
锁对象与同步代码块一样
lock锁
创建出锁对象进行加锁和解锁
ReentrantLock()获得lock对象
private final Lock lk = new ReentrantLock();
lk.lock();上锁
try{
}finally{
方finally里进行解锁
lk.unlock();
}
线程池
可以复用线程的技术
创建线程池
方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。
方式二:使用Executor(线程池的工具类)调用方法返回不同特点的线程池对象。
- 参数一:corePoolSize:指定线程池的核心线程的数量。
- 参数二:maximumPoolSize:指定线程池的最大线程数量
- 参数三:keepAliveTime:指定临时线程的存活时间
- 参数四:unit:指定临时线程存活的时间单位
- 参数五:workQueue:指定线程池的任务队列
- 参数六:threadFactory:指定线程池的线程工厂
- 参数七:handler:指定线程池的任务拒绝策略
处理Runnable任务
void execute(Runnable command)
什么时候才会创建临时线程?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务
处理Callable任务
Future<T> submit(Callable<T> task)