多线程
进程:
本地开启一个客户端,就是一个进程 "任务管理器"!
能够调用系统资源的独立单位
线程:
线程是依赖于进程的,将线程看成是进程中某个"任务"
多进程的意义?
多进程的意义:就是为了提高cpu的使用率!
多线程的意义呢?
每个线程为了抢占CPU的执行权,他们的抢占过程中具有随机性!
jvm:是假想计算机---开启对应进程
jvm:里面至少有两条线程
main方法: 主线程----"用户线程"
垃圾回收线程:产生很多对象的时候,需要通过gc回收没有更多引用的对象!
jvm虚拟机的启动是单线程的还是多线程的?
多线程的。
原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。
现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。
如何实现一个多线程程序
如何实现一个多线程程序?
要实现多线程---线程是依赖于进程,创建进程---->创建系统资源
但是Java语言不能够直接创建系统的,JDK提供一个类:Thread
线程类
jvm是可以运行多个线程并发的执行
并发:在同一个时间点同时发生的!
方式1:
1)自定义一个类 继承Thread类 :线程类
2)重写Thread类中的run方法
3)在当前用户线程中(main)中创建该类对象,
启动线程 :start()方法
方式2(推荐):
1)自定义一个类(资源类),这个类实现接口:Runnable接口---用到了Java设计模式之代理模式!
2)重写Runnable接口中的run方法
3)在主线程中main
3.1)创建当前资源类对象,
3.2)在创建Thread类对象,将3.2中的资源类对象作为参数进行传递
4)启动线程 start方法启动
方式3:线程池
1)静态功能,引入工厂
public static ExecutorService newFixedThreadPool(int nThreads)
2)提交异步任务
Future submit(Callable<T> call)
3)关闭线程池
shutDown()
多线程中的一些基本方法
设置线程的名称
public final void setName(String name)
获取线程的名称
public final String getName()
获取主线程中名称
public static Thread currentThread():表示当前正在运行的线程
getName():获取线程名称
setName(String name):设置当前线程名称
public final void join() throws InterruptedException:等待该线程终止
Java开启线程
Java能够开启线程吗?
Java不能够直接开启线程的!
start方法---通过JVM调用
start方法本身就是同步方法----线程安全的方法
将所有的开启的线程(在运行之前),将线程添加到线程组中:ThreadGroup:一个线程组中包含多个线程
调用start0():间接实现的
本地方法----C++语言实现 !
run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
线程的优先级
线程有两种调度模型:
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获 取的 CPU 时间片相对多一些。
注:Java使用的是抢占式调度模型
设置线程的优先级
public final void setPriority(int newPriority)
获取线程的优先级
public final int getPriority()
线程的优先级: 常量值
public static final int MAX_PRIORITY:最大优先级 10
public static final int MIN_PRIORITY:最小优先级 1
public static final int NORM_PRIORITY:默认优先级 5
注: 优先级越大的线程抢占CPU的执行权几率大
优先级越小的线程抢占CPU的执行几率小
符号线程的执行具有随机性
线程的状态
线程的生命周期/线程的状态
State:线程的状态:是Thread类内部枚举
NEW,线程新建状态
RUNNABLE,线程运行状态
BLOCKED,线程阻塞状态(sleep(),等待)
WAITING,死死等待
TIMED_WAITING,超时等待 等待到一定时间,不等了
TERMINATED;线程终止!
Thread类的功能
JDK提供内置注解:标记方法是否以过时!
中断线程
public final void stop():已过时的方法,但是可以使用!强迫线程停止执行
public void interrupt():中断线程 :中断线程的一种状态!(6种状态),
打破了状态之后,继续执行程序!
线程休眠
public static void sleep(long millis) throws InterruptedException
线程睡眠 (阻塞式方法!)
参数为时间毫秒值!
线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程!
理解为"谦让"
线程的执行随机性
线程加入
public final void join():等待该线程终止。
设置守护线程
public final void setDaemon(boolean on):设置守护线程
在启动线程之前,调用这个方法才能设置为守护线程 (注意事项!)
(如果当前运行中线程都是守护线程,JVM退出)
多线程的安全问题
多线程的安全问题如何解决呢?
检验多线程安全问题的标准:
1)查看当前环境是否是多线程环境 是
2)当前程序中是否存在共享数据 存在
3)是否有多条语句对共享数据进行操作 存在
为了解决这个问题:
Java提供了一个关键字:synchronized
同步代码块/同步方法(将synchronized添加到方法上)
synchronized(锁对象){
多条语句对共享数据进行操作
}
锁对象:多个线程应该使用的是同一把锁!
注:如果一个方法进来是一个同步代码块,-----同步方法(非静态):将synchronized放在方法声明上
格式
权限修饰符 synchronized 返回值类型 方法名(形式参数列表)
如果通用的方式:synchronized同步代码块/同步方法解决线程安全问题,但是可能会出现一种死锁!
死锁:加入同步之后,两个或者两个以上的线程在互相抢占资源的时候出现一种互相等待的情况!
解决:加入生产者消费者模式(等待唤醒机制)
Lock接口
juc包下的接口
Lock接口提供一些相关的功能,比synchronized有具体的锁定操作(具体的功能
获取锁/释放锁)
Lock不能直接实例化:
具体的子实现类ReentrantLock
private Lock lock = new ReentrantLock();
功能:
public void lock():获取锁
public void unlock():释放锁
线程组:ThreadGroup
线程组:ThreadGroup:
一个线程的集合!
Thread类中:
public final ThreadGroup getThreadGroup():获取当前线程所属的线程组
Thread类中
Thread(ThreadGroup th,String name)
ThreadGroup
public final String getName() :获取线程组名称
线程组中包含很多个线程,每一个线程默认的线程组名称:main
线程启动过程: start()---->当前所有启动的线程添加到线程组中了
将所有的线程添加线程组之后,线程执行完毕了,变成垃圾--->被回收
线程池----多线程的第三种实现方式
线程池----多线程的第三种实现方式
创建一个固定的可重用的线程数,当这些线程使用完毕终止了,它(线程对象)不会被回收掉
归还到线程池中,下次在利用!
注:ExecutorService 接口:不能实例化
间接通过:
java.util.concurrent 类 Executors :线程池的工厂类
23种设计模式(思想)---- 创建型设计模式中一种 "静态工厂方法模式"
工厂类中一些静态功能:返回值 ExecutorService
创建线程池步骤:
创建一个可重用的,具有固定线程数的线程池:
public static ExecutorService newFixedThreadPool(int nThreads)
可以执行Runnable对象或者Callable对象代表的线程:提交异步任务(执行)
<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable<T> task)
例:pool.submit(new MyRunnable()
结束线程池:
void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务
例:pool.shutdown();
Future:
V get():获取当前计算结果的值
返回值:表示异步计算的结果
数据库--连接池
后面: 每次连接数据库的时候---产生Connection:连接对象
提供一些第三方:德鲁伊(druid:阿里的),cp30,dbcp
当前创建一个连接池:固定的可重复利用的Connection对象,每次使用完毕close()--将它归还到连接池中(参数:最大连接数量10,最小连接数量,
数据库连接对象查过最大连接数量的超时间,数据库的密码,用户名,连接url地址...)
池化技术!
nitify()和wait()为什么不定义在Tread类中而定义在Objiect类中
notify()和wait()---底层存在一种监视器(一种所对象状态的变化)--本地方法实现
这两个都是由锁对象来访问,锁对象---可以任意java类对象
wait()方法调用的时候,会立即释放锁
sleep()和wait()方法的区别
共同点:
都会抛出中断异常:任何线程中断了当前那个现在的这种状态
不同点:
1)是否立即释放锁:
sleep(long time):方法的调用---等待休眠时间到了,计时等待,不会释放锁对象---释放的是一种锁状态
wait():该方法调用会立即释放所对象,然后后面才能使用所对象调用唤醒对方线程
2)来源不同
sleep():来源于Thread类
wait():来源于Object类,wait()和锁息息相关
run()和start()的区别
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法
同步机制和等待唤醒机制
同步机制:
所有的多线程中资源冲突问题,都使用同步代码块/同步方法,
给当前对资源共享的数据上一道锁
格式:
synchronized(锁对象){
对象资源共享数据的操作
}
等待唤醒机制:(生产者消费者模式---信号法:wait方法+notify方法)
生产者不断的产生数据,需要等待wait()消费者线程使用数据,
使用完毕之后,没有数据了,需要通知生产者线程产生数据(notify())
封装资源数据:Student
标记值:boolean flag; 默认false:没有数据,true:存在数据!
静态代理
静态代理
1)真实角色只专注于自己的事情
2)找一个代理类---代理角色来完成真实角色完成不了的事情
代理角色和真实角色都需要实现同一个接口 (Thread类:多线程的实现方式2)
Tread类:真实角色----启动线程(完成自己的事情):系统资源启动
自定义类:MyRunnable implements Runnable{} 代理角色
代理角色重写run方法()
完成代理的业务...
线程匿名内部类的方式
线程匿名内部类的方式
new 类名/接口名(){
重写方法
};
匿名内部类的本质:
继承了该类或者实现了该接口子类的对象
匿名内部类的好处----降低了耦合性!(解耦)
定时器:Timer
java.util.Timer: 定时器工具
可以重复执行某个任务/或者执行一次任务
构造方法
public Timer():构造一个新的计时器
成员方法:
public void cancel():终止定时器
String日期文本--->Date日期格式:
public void schedule(TimerTask task,Date time):在指定日期时间内容将执行这个 任务!
public void schedule(TimerTask task, long delay):在指定时间后执行当前这个task任务
public void schedule(TimerTask task, long delay,long period):
在某个时间内容执行这个任务,然后每经过period时间毫秒值后重复执行任务!
TimerTask:由定时器Timer来安排是执行一次/重复执行当前任务
public abstract void run():任务要执行的操作