常州网站建设公司巧誉友网络,在线代理浏览网页,电脑租赁,做网站开发的流程文章目录 OS中的基本概念进程#xff08;process#xff09;与线程#xff08;thread#xff09;串行(serial)、并行(parallel)与并发(concurrency)同步(synchronization)与异步(asynchronization) java程序运行原理java命令主类类名运行原理 多线程的实现方式一#xff1… 文章目录 OS中的基本概念进程process与线程thread串行(serial)、并行(parallel)与并发(concurrency)同步(synchronization)与异步(asynchronization) java程序运行原理java命令主类类名运行原理 多线程的实现方式一继承Thread类获取和设置线程的名称 线程的调度方式调度方式的分类java中采用哪种调度方式 线程的优先级线程控制API线程休眠sleep线程合并join线程礼让yield守护线程setDaemon线程中断stop已过时 线程的生命周期线程的几种状态 多线程实现方式二:实现Runnable接口方式一 VS 方式二多线程数据安全问题出现的情况产生原因 解决多线程数据安全问题synchronizedsynchronized的细节问题 Lock锁ReentrantLock可重入锁 死锁生产者消费者模型线程间通信wait与notify机制wait()notify()notifyAll()为什么wait,notify,notifyAll方法不定义在Thread类中? Java的代码都是在某一条执行路径下按照顺序依次执行的一条路径就是一个线程。 OS中的基本概念
进程process与线程thread
进程
进程是操作系统进行资源调度与分配的基本单位正在运行的程序或者软件
线程
进程中有多个子任务,每个子任务就是一个线程。从执行路径的角度看, 一条执行路径就是一个线程线程是CPU进行资源调度与分配的基本单位
进程与线程的关系
线程依赖于进程而存在一个进程中可以有多个线程(最少1个)线程共享进程资源举例: 迅雷, Word拼写
串行(serial)、并行(parallel)与并发(concurrency)
串行
一个任务接一个任务按顺序执行
并行
在同一个时间点(时刻)上, 多个任务同时运行
并发
在同一时间段内,多个任务同时运行 同步(synchronization)与异步(asynchronization) 单道批处理内存中只能运行一个进程
多道批处理内存中可以运行多个进程, 同时发生 (进程的上下文切换)
进程的上下文切换 保护现场 恢复现场
现代操作系统引入了线程
java程序运行原理
java命令主类类名运行原理
java命令会启动jvm进程, jvm进程会创建一个线程(main线程)执行main线程里面的main方法jvm是多线程的。除了main线程外,还有其他线程,起码还有一个垃圾回收线程 多线程的实现方式一继承Thread类
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。 步骤
定义一个类继承Thread类重写run方法创建子类对象通过start方法启动线程
eg public class Demo {public static void main(String[] args) {System.out.println(start);// 3. 创建子类对象MyThread myThread new MyThread();// 4. 通过start方法启动线程myThread.start();System.out.println(end);}
}/*多线程的实现方式一继承Thread*/// 1. 定义一个类继承Thread类
class MyThread extends Thread {// 2. 重写run方法Overridepublic void run() {// 放的是在线程中要执行的代码System.out.println(nihao);}
} 注两条路径执行会得到这个结果如果想end和nihao颠倒过来在输出end语句上面增加一个睡眠TimeUnit.SECONDS.sleep(1);
注意事项
多线程的执行特点是随机的start方法跟run方法有什么区别? start方法才是开辟新的执行路径。run方法只是普通方法调用, 并没有开辟新的执行路径, 还是一条执行路径, 仍然是单线程的。 同一个线程能否启动多次? 不能启动多次, 会产生java.lang.IllegalThreadStateException的异常报错 谁才代表一个线程? Thread及其子类对象才代表线程, 就是t1t2
获取和设置线程的名称
获取名称 设置名称 线程的调度方式
给线程分配CPU处理权的过程
调度方式的分类
协同式线程调度 线程的执行时间由线程本身决定, 当这个线程执行完后报告操作系统,切换到下一个线程 抢占式的线程调度方式 线程的执行时间由系统决定 哪个线程抢到了CPU的执行哪个线程执行
java中采用哪种调度方式
Java中采用的是抢占式的调度方式
线程的优先级 注
MAX_PRIORITY 线程可以具有的最高优先级。10MIN_PRIORITY 线程可以具有的最低优先级。1NORM_PRIORITY 分配给线程的默认优先级。 5 结论
优先级没有什么用它仅仅只能被看做是一种建议(对操作系统的建议) 实际上操作系统本身有它自己的一套线程优先级 (静态优先级 动态优先级) 线程控制API
线程休眠sleep
暂停执行的作用 注 TimeUnit.SECONDS.sleep(1);和Thread.sleep(1000);是等价的只不过后者单位是毫秒前者单位是秒
线程合并join
等待该线程终止。 执行结果上看等待的是子线程, 哪个线程调用了join, 等待的就是这个线程。 eg public class Demo {public static void main(String[] args) {// 创建线程对象MyThread1 thread1 new MyThread1();// 启动startthread1.start();// 使用join方法try {thread1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}// main函数打印3个数for (int i 0; i 3; i) {System.out.println(Thread.currentThread().getName() ---- i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}class MyThread1 extends Thread {Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(getName() ---- i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
线程礼让yield 注虽然yield方法使当前线程放弃了CPU的执行权 但是仍然可以参与下轮的CPU的竞争。
eg public class Demo {public static void main(String[] args) {YieldThread a new YieldThread(A);YieldThread b new YieldThread(B);a.start();b.start();}
}class YieldThread extends Thread{public YieldThread(String name) {super(name);}Overridepublic void run() {for (int i 0; i 3; i) {System.out.println(this.getName() ---- i);// 暂停当前正在执行的线程对象并执行其他线程。Thread.yield();}}
}
守护线程setDaemon
线程分类
用户线程(默认) 系统的工作线程 守护线程 为用户线程服务的线程(GC垃圾回收线程)系统的后台线程可以把它当做用户线程的奴仆。 on - 如果为 true则将该线程标记为守护线程。
注意事项
当正在运行的线程都是守护线程时Java 虚拟机退出。该方法必须在启动线程前调用。 如果在start之后写出则会报错 — java.lang.IllegalThreadStateException
线程中断stop已过时 该方法具有固有的不安全性
eg
需求 定义一个flag标记, true 是正常状态 false中断 主线程打印3个数 打印1个 休眠1秒 中断子线程 创建子线程 打印10个数 休眠1秒 打印之前判断一下是否中断 如果正常---- 打印数据 如果发生了中断------- 不在打印, 并且把中断信息保存到log.txt文件中 格式年月日 时分秒 哪个线程发生了中断 public class Demo {public static void main(String[] args) {// 创建线程对象ThreadStop threadStop new ThreadStop();// 启动线程threadStop.start();for (int i 0; i 3; i) {System.out.println(main ---- i);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}threadStop.flag false;}
}class ThreadStop extends Thread {// 定义一个标记flagboolean flag true;Overridepublic void run() {for (int i 0; i 10; i) {// 判断线程的状态if (flag) {// 如果正常---- 打印数据System.out.println(this.getName() ---- i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}} else {// 创建字符输出流对象FileWriter fileWriter null;try {// 如果发生了中断------- 不在打印, 并且把中断信息保存到log.txt文件中fileWriter new FileWriter(log.txt);// 创建SimpleDataFormat对象指定日期格式SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);// write(String s)fileWriter.write(sdf.format(new Date()) getName() 发生了中断);fileWriter.flush();} catch (IOException e) {e.printStackTrace();} finally {if (fileWriter ! null) {try {// close 释放资源fileWriter.close();} catch (IOException e) {e.printStackTrace();}}}}}}
} 线程的生命周期
线程的几种状态
理论层面上
新建
刚new出来的线程对象
就绪
线程执行了start()方法后
执行
拥有CPU的执行权
阻塞
线程会处于阻塞状态
死亡
run方法执行完
代码层面上 线程状态的转换 多线程实现方式二:实现Runnable接口 步骤
定义一个类实现Runnable接口重写run方法创建子类对象创建线程对象, 把实现了Runnable接口的子类对象作为参数传递start方法启动线程
使用的构造方法 eg public class Demo {public static void main(String[] args) {/*多线程的实现方式二实现Runnable接口*/// 3. 创建子类对象MyRunnable myRunnable new MyRunnable();// 4. 创建线程对象, 把实现了Runnable接口的子类对象作为参数传递Thread thread new Thread(myRunnable);// 5. start方法启动线程thread.start();}
}// 1. 定义一个类实现Runnable接口
class MyRunnable implements Runnable{// 2. 重写run方法Overridepublic void run() {System.out.println(son of thread is running!);}
}
eg也可以使用lambda表达式或者匿名内部类 // 匿名内部类
new Thread(new Runnable() {Overridepublic void run() {System.out.println(1111);}
}).start();// lambda
new Thread(()-{System.out.println(222222);
}).start();
Runnable中的run方法为什么会运行在子线程中
见下面的伪代码 class Thread{
// 成员变量private Runnable target;// 构造方法Thread(Runnable target){init(target);}void init(){// 左边是成员变量 右边是传过来的参数 给成员变量赋值this.target target;}void run(){if(target ! null){target.run()}}
} 方式一 VS 方式二
步骤上, 方式一4步, 方式二是5步方式一通过继承的方式(单继承的局限性),方式二通过实现接口的方式方式二把线程跟线程上要做的事情区分开来(执行路径,跟执行路径上的任务区分开来) 解耦方式二便于数据共享
eg public class Demo {public static void main(String[] args) {MyShare runnable new MyShare();Thread thread1 new Thread(runnable);Thread thread2 new Thread(runnable);Thread thread3 new Thread(runnable);thread1.setName(window No.1);thread2.setName(window No.2);thread3.setName(window No.3);thread1.start();thread2.start();thread3.start();}
}class MyShare implements Runnable{int ticket 100;Overridepublic void run() {while(true){if(ticket 0){// 模拟网络时延try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() sell No. (ticket --) ticket);}}}
} 多线程数据安全问题
出现的情况
重复情况 出现不存在的数据
产生原因
多线程的运行环境需求多线程共享数据需求存在非原子操作可以进行操作 原子操作 一个不可分割的操作一个操作要么一次执行完, 要么不执行
解决多线程数据安全问题
synchronized
同步代码块
同步代码块的锁对象(对象 , 用来充当锁的角色)锁对象可以是任意的java对象, 但是要保证是同一个。
语法 synchronized(锁对象){// 对共享数据的访问操作
}
修改后为 public class Demo {public static void main(String[] args) {MyShare runnable new MyShare();Thread thread1 new Thread(runnable);Thread thread2 new Thread(runnable);Thread thread3 new Thread(runnable);thread1.setName(window No.1);thread2.setName(window No.2);thread3.setName(window No.3);thread1.start();thread2.start();thread3.start();}
}class MyShare implements Runnable {int ticket 100;// 定义一把锁Object object new Object();Overridepublic void run() {while (true) {synchronized (object) {if (ticket 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() sell No. (ticket--) ticket);}}}}
}
同步方法
同步方法的锁对象是this
eg
private synchronized void sell() {if (ticket 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() sell No. (ticket--) ticket);}}静态同步方法
静态同步方法的锁对象是字节码文件对象(Class对象) 字节码文件对象的获取方法 对象.getClass()或者类名.class
eg
private static synchronized void sell() {if (ticket 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() sell No. (ticket--) ticket);}}}// 上面的同步代码块是
synchronized (MyShare.getClass()); // MyShare是创建的对象synchronized的细节问题
执行流程 A、B 2个线程访问synchronized代码块中的内容假设A线程抢到了CPU的执行权, 看一下锁对象是否可用如果可用, A线程就持有了锁对象, A线程访问同步代码块的内容A还没有访问结束,发生了线程切换,B抢到了执行权,B也想访问同步代码块中的内容, 看一下锁是否可用, 不可用, 对于B线程来说, 只能在synchronized外面等待, B就处于同步阻塞状态A再次抢到执行权. A接着执行,访问结束, 退出synchronized代码块, A释放锁B线程就可以获取锁, 访问synchronized代码块中的内容.
eg
public class Demo {// 定义一把锁public static final Object obj new Object();public static void main(String[] args) {// 定义线程Anew Thread(() - {synchronized (obj) {System.out.println(进入线程A);try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(退出线程A);}},A).start();// main休眠1stry {System.out.println(main函数休眠1s);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 定义线程Bnew Thread(() - {synchronized (obj) {System.out.println(进入线程B);try {TimeUnit.SECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(退出线程B);}},B).start();}
}出现异常就会释放锁
eg public class Demo {// 定义一把锁public static final Object OBJECT new Object();public static int count 0;public static void main(String[] args) {// 定义线程Anew Thread(() - {System.out.println(进入线程A);synchronized (OBJECT) {while (true) {count;System.out.println(A进入到synchronized中);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}if(count 5){System.out.println(count count);// 人为制造一个异常System.out.println(10/0);}}}}, A).start();// main休眠1stry {System.out.println(main函数休眠1s);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 定义线程Bnew Thread(() - {System.out.println(进入线程B);synchronized (OBJECT) {System.out.println(B进入到了synchronized中);}System.out.println(退出线程B);}, B).start();}
}
2条字节码指令(monitorenter/monitorexit) Lock锁
它是一个接口 方法 ReentrantLock可重入锁
一个可重入的互斥锁 Lock它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义但功能更强大。
eg public class Demo {public static void main(String[] args) {MyShare1 runnable new MyShare1();Thread thread1 new Thread(runnable);Thread thread2 new Thread(runnable);Thread thread3 new Thread(runnable);thread1.setName(window No.1);thread2.setName(window No.2);thread3.setName(window No.3);thread1.start();thread2.start();thread3.start();}
}class MyShare1 implements Runnable {int ticket 100;//定义一把Lock锁Lock lock new ReentrantLock();Overridepublic void run() {while (true) {// 获取锁// lock()lock.lock();try{if (ticket 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() sell No. (ticket--) ticket);}}finally {// 释放锁// unlock()lock.unlock();}}}
}
synchronized VS Lock
synchronized是关键字 Lock是个接口synchronized是一把隐式的锁, 加锁和释放锁是由jvm自动完成的。Lock它是一把真正的(显式的)锁, 我们能看到加锁跟释放锁的过程(lock , unlock) 死锁
2个或以上线程因为争抢资源而造成的互相等待的现象
发生的场景一般出现在同步代码块嵌套
语法
synchronized(objA){synchronized(objB){
}
}eg /*
模拟死锁产生的场景*/public class Demo {public static void main(String[] args) {new Thread(new DieLock(true)).start();new Thread(new DieLock(false)).start();}
}// 定义一个锁类
class MyLock {public static final Object OBJECTA new Object();public static final Object OBJECTB new Object();
}class DieLock implements Runnable {// 定义一个flagboolean flag true;public DieLock(boolean flag) {this.flag flag;}Overridepublic void run() {if (flag) {synchronized (MyLock.OBJECTA) {System.out.println(if A);synchronized (MyLock.OBJECTB) {System.out.println(if B);}}} else {synchronized (MyLock.OBJECTB) {System.out.println(else A);synchronized (MyLock.OBJECTA) {System.out.println(else B);}}}}
}
解决死锁的办法
使加锁的顺序保持一致
eg Overridepublic void run() {if (flag) {synchronized (MyLock.OBJECTA) {System.out.println(if A);synchronized (MyLock.OBJECTB) {System.out.println(if B);}}} else {synchronized (MyLock.OBJECTA) {System.out.println(else A);synchronized (MyLock.OBJECTB) {System.out.println(else B);}}}}再加一把锁使其变成原子操作
eg
Overridepublic void run() {if (flag) {synchronized (MyLock.OBJECT) {synchronized (MyLock.OBJECTA) {System.out.println(if A);synchronized (MyLock.OBJECTB) {System.out.println(if B);}}}} else {synchronized (MyLock.OBJECT) {synchronized (MyLock.OBJECTB) {System.out.println(else A);synchronized (MyLock.OBJECTA) {System.out.println(else B);}}}}}生产者消费者模型 同步代码块版本 // 蒸笼类
/*
使用同步代码块*/// 蒸笼类
public class Box {// 定义成员Food food;// 生产包子的方法 只要生产者会执行public void makeFood(Food newFood){food newFood;System.out.println(Thread.currentThread().getName() 生产了 food);}// 吃包子的方法 只要消费者会执行public void eatFood(){System.out.println(Thread.currentThread().getName() 吃了 food);food null;}// 判断蒸笼状态的方法public boolean isEmpty(){return food null;}}// 定义包子类
class Food{String name;int price;public Food(String name, int price) {this.name name;this.price price;}Overridepublic String toString() {return Food{ name name \ , price price };}
}// 生产者任务
public class ProducerTask implements Runnable{// 定义一个成员Box box;Food[] foods {new Food(pork,3),new Food(mutton,4),new Food(tomato,2)};Random random new Random();public ProducerTask(Box box) {this.box box;}Overridepublic void run() {// 生产包子while (true){// 使用synchronizedsynchronized (box){// 按照上图逻辑if(box.isEmpty()){// 如果蒸笼为空,没有包子生产包子 放进去int index random.nextInt(foods.length);box.makeFood(foods[index]);box.notify();}else{try {box.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}
}// 消费者任务
public class ConsumerTask implements Runnable{Box box;public ConsumerTask(Box box) {this.box box;}Overridepublic void run() {// 吃包子while (true){synchronized (box){if(box.isEmpty()){try {box.wait();} catch (InterruptedException e) {e.printStackTrace();}}else{box.eatFood();box.notify();}}}}
}// 运行
public class Demo {public static void main(String[] args) {// 创建一个Box对象Box box new Box();// 创建生产者任务ProducerTask producerTask new ProducerTask(box);// 创建消费者任务ConsumerTask consumerTask new ConsumerTask(box);// 创建生产者线程Thread t1 new Thread(producerTask,生产者线程);// 创建消费者线程Thread t2 new Thread(consumerTask,消费者线程);// start启动线程t1.start();t2.start();}
}同步方法版本 /*
使用同步方法*/// 蒸笼类
public class Box {// 定义成员Food food;// 生产包子的方法 只要生产者会执行public synchronized void makeFood(Food newFood){if (food null){food newFood;System.out.println(Thread.currentThread().getName() 生产了 food);this.notify();}else{try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}// 吃包子的方法 只要消费者会执行public synchronized void eatFood(){if(food null){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}else{System.out.println(Thread.currentThread().getName() 吃了 food);food null;this.notify();}}
}// 定义包子类
class Food{String name;int price;public Food(String name, int price) {this.name name;this.price price;}Overridepublic String toString() {return Food{ name name \ , price price };}
}// 生产者任务
public class ProducerTask implements Runnable {// 定义一个成员Box box;Food[] foods {new Food(pork, 3),new Food(mutton, 4),new Food(tomato, 2)};Random random new Random();public ProducerTask(Box box) {this.box box;}Overridepublic void run() {// 生产包子while (true) {// 按照上图逻辑// 如果蒸笼为空,没有包子生产包子 放进去int index random.nextInt(foods.length);box.makeFood(foods[index]);}}
}// 消费者任务
public class ConsumerTask implements Runnable {Box box;public ConsumerTask(Box box) {this.box box;}Overridepublic void run() {// 吃包子while (true) {box.eatFood();}}
}public class Demo {public static void main(String[] args) {// 创建一个Box对象Box box new Box();// 创建生产者任务ProducerTask producerTask new ProducerTask(box);// 创建消费者任务ConsumerTask consumerTask new ConsumerTask(box);// 创建生产者线程Thread t1 new Thread(producerTask,生产者线程);// 创建消费者线程Thread t2 new Thread(consumerTask,消费者线程);// start启动线程t1.start();t2.start();}
}当有多个生产 多个消费者的时候, 出现卡顿的现象, 怎么解决?
答使用notifyAll 线程间通信
wait与notify机制 作用
拥有相同锁的线程才可以实现wait/notify机制wait()方法使线程暂停运行而notify() 方法通知暂停的线程继续运行wait会释放锁notify不会释放锁
wait()
阻塞功能 当在某线程中对象上.wait(), 在哪个线程中调用wait(), 导致哪个线程处于阻塞状态当某线程因为调用执行某对象的wait(),而处于阻塞状态我们说该线程在该对象上阻塞。 唤醒条件 当某线程因为某对象A的wait(), 而处于阻塞状态时如果要唤醒该线程只能在其他线程中 再同一个对象(即对象A)上调用其notify()或notifyAll()即在线程的阻塞对象上调用notify或notifyAll方法才能唤醒在该对象上阻塞的线程 运行条件 当前线程必须拥有此对象监视器。 监视器指synchronized代码块中的锁对象 即我们只能在当前线程所持有的synchronized代码块中的锁对象上调用wait方法 才能正常执行如果没有锁对象就会有这样一个异常IllegalMonitorStateException 执行特征 a.该线程发布(release)对此监视器的所有权b.等待(阻塞)注意Thread的sleep方法执行的时候该线程不丢失任何监视器的所属权
wait方法的使用条件必须有synchronized在锁对象上调用wait
wait方法的执行特征使当前线程暂停执行处于阻塞状态释放锁
eg public class Demo {// 定义一把锁public static final Object OBJECT new Object();public static void main(String[] args) {// 创建并启动一个线程new Thread(()-{System.out.println(A is running);synchronized (OBJECT){System.out.println(进入A的同步代码块);try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}// 使用wait方法try {System.out.println(wait before);OBJECT.wait();System.out.println(wait after);} catch (InterruptedException e) {e.printStackTrace();}}},A).start();// main休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 创建并启动一个线程new Thread(()-{System.out.println(B is running);synchronized (OBJECT){System.out.println(进入B的同步代码块);// 执行notify方法System.out.println(notify before);OBJECT.notify();System.out.println(notify after);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}},B).start();}
}notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待则会选择唤醒其中一个线程。选择是任意性的
notifyAll()
唤醒多个等待的线程
为什么wait,notify,notifyAll方法不定义在Thread类中?
任意Java对象都能充当锁的角色