多线程
多任务执行,多条路径可以执行
三高:高可用,高并发,高性能
thread 类
线程是程序中执行的线程。Java虚拟机允许应用程序同时运行多个执行线程。
多线程的优点: 提高效率
进程和线程的区别:
进程:系统中的应用程序,一个进程之间包含一到多个线程,进程具有自己的资源,内存空间,进程之间是资源分配的最小单位。
线程:一个程序中的顺序流,多个 线程共享一个进程的资源和数据空间,每一个线程具有自己的程序计数器,线程是cpu调度的最小单位
线程的创建
1.继承Thread,重写run()方法,定义线程体 + start()
public static void main(String[] args) {
//创建线程
Class001_Thread th = new Class001_Thread();
//开启线程 start() 可以被cpu调度了
th.start();
for(int i=1;i<=20;i++){
System.out.println("一边讲课....");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//思考:
//1) 不使用start()开启线程,直接调用run()方法
//2)start开启位置是否可以修改?
}
/*
run() 定义线程体
*/
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边喝水....");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2 实现Runnable接口,重写run方法 + start()
1.类是单继承,接口多实现
2.可以实现资源共享
public class Class002_Thread implements Runnable{
public static void main(String[] args) {
//创建线程
Thread th = new Thread(new Class002_Thread());
//开启线程
th.start();
//new Class002_Thread().run(); //方法的调用,不是线程的开启
for(int i=1;i<=20;i++){
System.out.println("一边陪女朋友");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
定义线程体
*/
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边打游戏");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.实现Callable,重写call(),在方法内部定义线程体 --> 了解
1.call可以抛出异常
2.可以定义返回值
public class Class006_Racer implements Callable<Integer> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//一场比赛
Class006_Racer racer = new Class006_Racer();
//1.创建执行服务
ExecutorService server = Executors.newFixedThreadPool(2);
//2.提交任务
Future<Integer> future1 = server.submit(racer);
Future<Integer> future2 = server.submit(racer);
//3.获取结果
System.out.println(future1.get());
System.out.println(future2.get());
//3.终止服务
server.shutdown();
}
模拟12306购票
100张票 3个人买完
共享的资源: 100张张票
多线程: 4个线程 1个主线程 3个线程买票
public class Class003_Web12306 implements Runnable{
int tickets = 100; //100张票
/*
每个线程购票的流程
*/
@Override
public void run() {
//重复买票
while(true){
//没有票停止购买
//A B C
if(tickets<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class003_Web12306 web = new Class003_Web12306();
//创建线程
Thread th1 = new Thread(web,"333");
Thread th2 = new Thread(web,"222");
Thread th3 = new Thread(web,"111");
th1.start();
th2.start();
th3.start();
}
}
使用多线程和Java工具包实现网络资源下载
public class Class004_TestWebDownloader{
public static void main(String[] args) {
Downloader th1 = new Downloader("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png","src/baidu.png");
Downloader th2 = new Downloader("https://img13.360buyimg.com/pop/s590x470_jfs/t1/191473/22/13064/97981/60efcd7fEde609b1a/8640695d031c2e4f.jpg","src/jd1.jpg");
Downloader th3 = new Downloader("https://imgcps.jd.com/ling4/3548676/6Ziy6Zuo6Ziy5rGb5b-F5aSH/54iG5qy-5L2O6IezOS455YWD/p-5bd8253082acdd181d02fa42/4f1be342/cr/s/q.jpg","src/jd2.jpg");
th1.start();
th2.start();
th3.start();
}
}
*/
/*
网络资源下载的JAVABEAN
*//*
class Downloader extends Thread{
private String url;
private String pathName;
public Downloader(String url, String pathName) {
this.url = url;
this.pathName = pathName;
}
@Override
public void run() {
//下载的过程
WebDownloader.down(url,pathName);
}
}
//网络资源下载工具类
class WebDownloader{
//网络资源下载步骤
public static void down(String url,String pathName){
try {
FileUtils.copyURLToFile(new URL(url),new File(pathName));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
模拟龟兔赛跑
兔子每跑十步休息一下 10毫秒
乌龟正常跑
只要有参赛者跑了100步就结束
使用多线程模拟比赛流程
public class Class005_Racer implements Runnable{
//赢的参赛者名字
private String winner = null;
public static void main(String[] args) {
//一场比赛
Class005_Racer racer = new Class005_Racer();
//参赛者
Thread th1 = new Thread(racer,"兔子");
Thread th2 = new Thread(racer,"乌龟");
//开始比赛
th1.start();
th2.start();
}
@Override
public void run() {
for(int steps=1;steps<=100;steps++){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在第"+steps+"步...");
//判断是否为兔子每跑十步休息一下 100毫秒
if("兔子".equals(Thread.currentThread().getName()) && steps%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//游戏是否结束,是否有参赛者赢了
if(checkOver(steps)){
break;
}
}
}
/**
* 检查游戏是否结束
* @param steps 当前参赛者的步数
* @return 是否结束
* true-->结束
* false -->不结束
*/
private boolean checkOver(int steps) {
if(winner!=null){
return true;
}
if(steps==100){
winner = Thread.currentThread().getName();
return true;
}
return false;
}
}
多线程的匿名内部类的使用
package com.xxxx.teacher.t01;
/*
* 其他定义线程体的方式
*/
public class Class007_Thread {
//内部类
static class Inner implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边吸烟...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Thread(new Inner()).start();
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一边烫头...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//lambda表达式
new Thread(() -> {
for(int i=1;i<=20;i++){
System.out.println("一边洗澡...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for(int i=1;i<=20;i++){
System.out.println("一边喝酒...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程的状态
- 新生状态 : new Thread(),当前线程处于新生状态
- 就绪状态 : start(),这个线程进入就绪状态,线程准备好可以被cpu调度,会进入到就绪队列,等待cpu的调度
- 运行状态 : 当cpu调度到这个线程,线程进入到运行状态开始执行
- 阻塞状态 : 当程序无法正常执行(进入阻塞状态有多种方式...)
- 终止状态 : 线程结束
注意:1.一个线程一旦终止,无法恢复
2.一个线程如果进入阻塞状态,阻塞接触会直接恢复到就绪状态
进入到阻塞状态的几种方式 :
1. sleep()
2. join()
3. wait()
4. IO
进入线程就绪状态的方式:
1. start()
2. yield()
3. 线程切换
4. 阻塞解除
进入终止状态的方式:
1.stop 已经过时,不推荐使用 2.正常之间完毕 3.添加标识判断 --> 推荐
sleep(ms) : 1s = 1000ms
线程休眠|睡眠
会进入到阻塞状态,当指定的ms数结束,线程会恢复到就绪状态
抱着资源睡觉
让出cpu的资源。抱着是指对象的资源|锁
作用 :
1) 模拟网络延迟 2) 方法问题的可能性
yield 礼让线程 高风亮节
当一个线程调用yield,让出cpu的资源,进入到就绪状态
public class Class002_Yield implements Runnable{
public static void main(String[] args) {
new Thread(new Class002_Yield(),"A").start();
new Thread(new Class002_Yield(),"B").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始了");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束了");
}
}
join() 插队线程
join(ms) 插队指定的ms数
成员方法join,线程对象.join() 当前线程对象插队
注意: 先就绪,后插队
public class Class003_Join {
public static void main(String[] args) {
new Thread(new Father()).start();
}
}
//父亲线程
class Father implements Runnable{
@Override
public void run() {
System.out.println("想抽烟了...");
System.out.println("给儿子钱,让他去买烟....");
//创建儿子线程
Thread th = new Thread(new Son());
//开启线程
th.start();
//插队
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("接过烟,吸一口...");
System.out.println("把零花钱给儿子....");
}
}
class Son implements Runnable{
@Override
public void run() {
System.out.println("接过钱,去买烟");
System.out.println("路上遇到一家游戏厅,进去玩10s钟...");
for(int i = 10;i>=0;i--){
System.out.println(i+"s过去了");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("赶紧去买烟...");
System.out.println("把烟递给老爸,然后把钱给他...");
}
}
interrupt() 为线程添加一个中断标识
- void interrupt() 为线程添加一个中断标识
- boolean isInterrupted() 测试此线程是否已被中断,是否已经调用过 interrupt()方法添加中断标识,是->true 不是->false
- static boolean interrupted() 测试当前线程是否已被中断, 是否已经调用过 interrupt()方法添加中断标识,是->true 不是->false,同时会复位标识
注意: 一个线程如果进入终止状态,线程的中断标识会复位
可能会出现的异常
sleep--> InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态 .
public class Class004_Interrupt implements Runnable{
@Override
public void run() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1;i<=100;i++){
if(Thread.interrupted()){
System.out.println("--------->"+Thread.currentThread().isInterrupted());
System.out.println("结束");
break;
}
System.out.println(i);
}
}
public static void main(String[] args) {
Thread th = new Thread(new Class004_Interrupt());
//就绪
th.start();
System.out.println(th.isInterrupted());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//为th线程添加中断标识
th.interrupt();
System.out.println(th.isInterrupted());
}
}
GetState() 获取当前线程的状态
Thread.State 线程状态。 线程可以处于以下状态之一:
- NEW 尚未启动的线程处于此状态。
- RUNNABLE 在Java虚拟机中执行的线程处于此状态。
- BLOCKED 被阻塞等待监视器锁定的线程处于此状态。多线程操作下,处于等待对象锁的线程处于这种状态
- WAITING 无限期等待另一个线程执行特定操作的线程处于此状态。 wait(),join()
- TIMED_WAITING 正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。阻塞与时间相关的线程处于这种状态 sleep(ms),join(ms),wait(ms)
- TERMINATED 已退出的线程处于此状态。
public class Class005_getState implements Runnable{
public static void main(String[] args) {
Thread th = new Thread(new Class005_getState());
System.out.println(th.getState()); //NEW
th.start();
System.out.println(th.getState()); //RUNNABLE
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--->"+th.getState());
//当th线程终止,主线程结束
if(Thread.State.TERMINATED.equals(th.getState())){
break;
}
}
}
@Override
public void run() {
for(int i=1;i<=10;i++){
if(i==5){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i);
}
}
}
Priority 线程的优先级
线程的优先级越高,有限执行的可能性越大
1~10 线程优先级的数值范围
默认5 NORM_PRIORITY
最小1 MIN_PRIORITY
最大10 MAX_PRIORITYsetPriority(int) 设置一个线程的优先级
getPriority() 获取一个线程的优先级
public class Class006_Priority implements Runnable{
public static void main(String[] args) {
Class006_Priority pri = new Class006_Priority();
Thread th1 = new Thread(pri,"A");
Thread th2 = new Thread(pri,"B");
Thread th3 = new Thread(pri,"C");
th1.setPriority(1);
th3.setPriority(Thread.MAX_PRIORITY);
System.out.println(th1.getPriority());
System.out.println(th2.getPriority());
System.out.println(th3.getPriority());
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
守护线程
线程:
用户线程
守护线程守护线程就是用来守护用户线程,当程序中所有的用户线程全部执行完毕,守护线程会直接结束
setDaeson(true) 设置守护线程
isDaeson() 判断线程是否为守护线程现在所创建的线程默认都是用户线程
垃圾回收机制典型的守护线程
public class Class007_Daemon{
public static void main(String[] args) {
Thread th = new Thread(()->{
int i = 1;
while(true){
System.out.println("守护线程"+i++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
/*设置为守护线程*/
th.setDaemon(true);
//开启线程
th.start();
for(int i=100;i<=110;i++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
System.out.println("主线程执行完毕");
}
}
线程安全问题
线程安全问题:
多线程同时操作同一份资源才有可能遇到线程不安全问题同步锁: 让关键重点代码排队执行,其他代码依旧同时执行,在提高效率的基础上保证数据安全
synchronized 关键字
关注点:
1.限制条件: 对象锁(每一个对象只有一把锁) 锁哪一个对象能锁住
2.同步的代码范围: 范围太大,效率低,范围太小,容易锁不住使用方式:
同步方法
静态方法
排队执行的代码范围: 方法体
锁的对象 : 锁类
成员方法 :
排队执行的代码范围: 方法体
锁的对象 : 调用成员方法的对象
同步块
synchronized(锁的对象){
排队执行的代码;
}
锁的对象 : this 类:类的Class对象 资源同步方法特点:
1.简单
2.范围太大,效率低
public class Class001_Web12306 implements Runnable{
int tickets = 100; //100张票
/*
同步方法
*/
@Override
public void run() {
//重复买票
while(true){
//没有票停止购买
//A B C
if(buyTicket()){
break;
}
/*方法问题的可能性*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 封装每一次购票的流程
* @return
* true: 结束购票
* false: 继续购买下一章
*/
public synchronized boolean buyTicket(){
if(tickets<=0){
return true;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
Class001_Web12306 web = new Class001_Web12306();
//创建线程
Thread th1 = new Thread(web,"333");
Thread th2 = new Thread(web,"222");
Thread th3 = new Thread(web,"111");
th1.start();
th2.start();
th3.start();
}
}
同步块
synchronized(对象){
同步代码段;
}
()-->
this
类
资源注意:
同步成员方法,相当于同步调用成员方法的对象,相当于同步this
成员方法中this指代调用成员方法的对象
锁对象相当于锁住了这个对象的所有资源|成员,如果只想要锁住某个资源,可以单独只锁资源
public class Class002_Web12306 implements Runnable{
int tickets = 100; //100张票
public void run() {
//重复买票
while(true){
System.out.println("--------");
System.out.println("--------");
System.out.println("--------");
System.out.println("--------");
//同步块
synchronized (this){
if(tickets<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class002_Web12306 web = new Class002_Web12306();
//创建线程
Thread th1 = new Thread(web,"333");
Thread th2 = new Thread(web,"222");
Thread th3 = new Thread(web,"111");
th1.start();
th2.start();
th3.start();
}
}
同步块
synchronized(对象){
同步代码段;
}
()-->
this
类
资源注意:
锁不变的内容
自定义引用数据类型对象地址不变
双重检查 double check可以提高效率
public class Class003_Web12306 implements Runnable{
Tickets tickets = new Tickets(); //自定义引用数据类型的对象
public void run() {
//重复买票
while(true){
//同步块
//ABC
if(tickets.num<=0){
break;
}
synchronized (tickets){
//双重检查 double check
if(tickets.num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets.num-- +"张票");
/* try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class003_Web12306 web = new Class003_Web12306();
//创建线程
Thread th1 = new Thread(web,"333");
Thread th2 = new Thread(web,"222");
Thread th3 = new Thread(web,"111");
th1.start();
th2.start();
th3.start();
}
}
//票
class Tickets{
int num = 100;
}
同步块
synchronized(对象){
同步代码段;
}
()-->
类 : 类的Class对象
获取类型的Class对象-->类名.class注意:
锁类相当于锁了这个类的所有对象,如果只想要锁当前类得到某一个对象,可以直接去锁对象,用哪个锁哪个
public class Class004_Web12306 implements Runnable{
Tickets tickets = new Tickets(); //自定义引用数据类型的对象
public void run() {
//重复买票
while(true){
synchronized (Class004_Web12306.class){
if(tickets.num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+ tickets.num-- +"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*多次购买之间休眠一定时间*/
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class004_Web12306 web = new Class004_Web12306();
//创建线程
Thread th1 = new Thread(new Class004_Web12306(),"333");
Thread th2 = new Thread(new Class004_Web12306(),"222");
Thread th3 = new Thread(new Class004_Web12306(),"111");
th1.start();
th2.start();
th3.start();
}
}