程序:一段静态的代码
进程:程序的一次执行过程,或是正在运行的一个程序
是一个动态的过程;有它自身的产生、存亡和消亡的过程---生命周期
系统在运行时会为每个进程分配不同的内存区域
线程:一个程序的一条执行路径,进程可进一步细化为线程
若一个进程同一时间并行执行多个线程,就是支持多线程的
每个线程拥有独立的虚拟机栈和程序计数器,线程切换的开销小
一个进程中的多个线程共享相同的内存空间,它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全隐患。
并行与并发:
并行:多个cpu同时执行多个任务。比如,多个人同时做不同的事情
并发:一个CPU同时执行多个任务。比如,秒杀,多个人做同一件事
方式一:继承java.lang.Thread类
1.创建一个继承Thread类的子类
2.重写run()方法
3.创建子类对象
4.调用start()方法
方式二:实现Runnable接口
1.创建一个实现Runnable接口的类
2.实现run()方法
3.创建实现类对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.调用start()方法
方式三:实现Callable接口
与Runnable相比,Callable功能更强大
Callable支持泛型
call()方法可以有返回值Object
call()方法可以抛出异常
Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等
FutureTask是Future接口唯一的实现类,FutureTask同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,也可以作为Future。
NumThread numThread = new NumThread(); //创建Callable接口实现类的对象 FutureTask futureTask = new FutureTask(numThread);//将此Callable接口实现类的对象传递到FutureTask的构造器中,创建FutureTask对象 new Thread(futureTask).start(); //将FutureTask对象传递到Thread类的构造器中,创建Thread对象才可以启动线程
方式四:使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
好处:
提升响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池 Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池 Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池 Executors.newScheduledThreadPool(n):创建一个线程池,可安排在给定延迟后运行或者定期地执行
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor来管理线程
void execute(Runnable command):执行任务,没有返回值,一般用来执行Runnable Future<T> submit(Callable<T> task):执行任务,有返回值,一般用来执行Callable void shutdown():关闭连接池
Thread类的常用方法
void start():启动线程,并执行对象的run()方法 run():线程在被调度时执行的操作 String getName():返回线程的名称 void setName(String name):设置该线程的名称 static Thread currentThread():返回当前线程。在Thread子类就是this static void yield():放弃当前线程CPU的执行权,把执行机会让给优先级相同或更高的线程,但下一刻可能还是会抢到执行权 join():在线程a中调用线程b的join(),此时线程a就进入到阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态 static void sleep(long millis):使当前线程在指定毫秒内放弃CPU的执行权,使其他线程有机会被执行,时间到后重新排队 stop():强制结束当前线程,已过时不推荐使用 boolean isAlive():判断线程是否还活着 getPriority():返回线程的优先级,默认优先级为5 setPriority(int newPriority):改变线程的优先级 //下面这三个方法只能用在synchronized里并且这三个方法的调用者必须是synchronized的同步监视器,这三个方法是Object类的,用于线程间的"通信" wait():使调用该方法的线程进行阻塞状态并释放同步监视器使其他线程可以继续操作 notify():唤醒一个被wait的线程 notifyAll():唤醒所有被wait的线程,
jdk中使用Thread.State类定义了线程的几种状态
新建:当Thread类或其子类被创建时,就处于新建状态
就绪:新建的线程被start()后,会等待CPU,此时已具备了运行条件
运行:就绪的线程获得CPU并被调度后,就进入运行状态
阻塞:因某种情况,运行的线程让出CPU并临时终止自己的执行
死亡:线程完成了工作、提前强制终止、出现异常导致结束
同步代码块:操作共享数据的代码完整声明在一个代码块中
synchronized(同步监视器){ //需要被同步的代码 }
同步方法:操作共享数据的代码完整声明在一个方法中
public synchronized void run(){}
Lock(锁):通过显式定义同步锁对象来实现同步。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
ReentrantLock类实现了Lock,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁。
要先创建ReentrantLock类对象,通过lock()锁定和unlock()解锁来实现线程安全
死锁
不同的线程分别占用对方需要的同步资源不放弃。都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会有提示,只是所有的线程都处于阻塞状态,无法继续。