进程和线程
多进程操作系统能同时运行多个进程(程序),由于CPU具备分时机制,所以每个进程都能循环获得自己的CPU时间片,由于CPU执行速度非常快,使得所有程序好像是在“同时”运行一样。
在同一个时间段上会有多个程序共同执行,而在一个时间点上只能有一个进程执行。
当用户使用java命令执行一个类的时候就表示启动了一个JVM的进程,而主方法是这个进程上的一个线程,类执行完毕,线程自动消失。每一个JVM运行时一个进程。
启动线程必须依靠Thread类中的start()方法执行,线程启动后默认调用run()方法
(在多线程操作中,使用start()方法启动多线程的操作是需要进行操作系统函数调用的,当一个线程对象被重复启动后会抛出该异常,一个线程对象只能启动一次)
1.Thread类实现
继承Thread类并重写run()方法,创建对象实现多线程。
2.Runable接口实现
实现Runable接口,重写接口中的run()方法,利用Thread类的构造方法,接收Runable子类对象,创建Thread对象实现多线程。
还可以通过Lambda表达式定义线程方法体
new Thread(()->{
for(int i=0;i<10;i++)
System.out.println("i");
}).start();
Thread和Runable区别
从JAVA的实际开发而言,使用Runable接口,采用采访时有效避免单继承的局限。
(Thread类也是Runable接口的子类)
并发访问设计:Thread负责线程调度,并发主体继承Runable接口
3.Callable接口实现
与Runable不同的是,该接口定义可设置一个泛型,call()方法有返回值,run()方法没有。
多线程运行状态
1.创建状态: 用构造方法创建一个线程对象后,新的线程对象便处于新建状态,此时它已经具有了相应的内存空间和其他资源,但还处于不可运行状态。
2.就绪状态:新建线程对象后,调用start()方法可以启动程序,启动后,线程进入就绪状态,线程进入线程队列排队,等待CPU调度服务,此时具备运行条件。
3.运行状态:线程被调用获得处理器资源时,进入运行状态,自动调用该线程对象的run()方法。
4.阻塞状态:在可运行状态下,调用sleep()/suspend()/wait()等都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除,才可转入就绪状态。
5.终止状态:run()方法结束运行时,进入终止状态,不具备继续运行的能力
多线程常用操作方法
1.setName()/getName():设置/取得线程名字(线程不命名时会自动命名)
2.sleep():设置线程休眠时间,时间一到自动唤醒
3.interrupt():中断线程执行/isInterrupted():是否中断
4.join():线程强制执行 强制执行结束后再执行其他线程代码
5.yield():线程礼让 当满足某些条件时,将当前的调度给其他线程使用,自己等待下次调度再执行。
6.线程优先级:MAX_PRIORITY:最高优先级10/NORM_/MIN_对应5/1
setPriority():设置线程优先级/getPriority():获得线程优先级
主方法线程优先级为5
线程同步和死锁
同步:造成并发资源访问不同步的主要原因在于没有将若干个逻辑单元进行整体性的锁定,即当判断数据和修改数据时只允许一个线程进行处理,而其他线程需要等待当前线程执行完毕后才可以继续执行。
synchronized关键字:为代码加"锁":同步代码块、同步方法
同步会造成处理性能下降,同时带来数据线程访问安全的优点。
死锁:本质是同一时间段内只允许有一个线程执行。
Object线程等待和唤醒机制(解决数据重复操作):
wait():线程等待
notify():唤醒第一个等待的线程执行
notifyAll():唤醒所有等待线程,优先级高执行。
当前不建议使用suspend(),resume(),stop()方法
后台守护线程
GC垃圾收集机制实际上就属于一个守护线程.
setDaemon():设置为守护线程
守护线程依附于用户线程存在,用户线程消失守护线程会跟着消失。
volatile关键字:不是描述同步的操作,而是更快捷进行原始变量的访问,避免了副本创建和数据同步处理,节约复制副本与同步的事件。