Java的代码都是在某一条执行路径下,按照顺序依次执行的,一条路径就是一个线程。
进程:
线程:
进程与线程的关系
串行
并行
并发
单道批处理:内存中只能运行一个进程
多道批处理:内存中可以运行多个进程, "同时"发生 (进程的上下文切换)
现代操作系统:引入了线程
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
步骤:
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方法
@Override
public void run() {
// 放的是在线程中要执行的代码
System.out.println("nihao");
}
}
注:两条路径执行会得到这个结果,如果想end
和nihao
颠倒过来,在输出end语句上面增加一个睡眠TimeUnit.SECONDS.sleep(1);
注意事项:
java.lang.IllegalThreadStateException
的异常报错获取名称:
设置名称:
给线程分配CPU处理权的过程
暂停执行的作用
注:
TimeUnit.SECONDS.sleep(1);
和Thread.sleep(1000);
是等价的,只不过后者单位是毫秒,前者单位是秒
等待该线程终止。
执行结果上看等待的是子线程, 哪个线程调用了join, 等待的就是这个线程。
eg:
public class Demo {
public static void main(String[] args) {
// 创建线程对象
MyThread1 thread1 = new MyThread1();
// 启动start
thread1.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 {
@Override
public 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方法
使当前线程放弃了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);
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(this.getName() + "----" + i);
// 暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield();
}
}
}
线程分类:
注意事项:
start
之后写出,则会报错 —> java.lang.IllegalThreadStateException
该方法具有固有的不安全性
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 {
// 定义一个标记flag
boolean flag = true;
@Override
public 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()
方法后执行
阻塞
死亡
run
方法执行完代码层面上:
线程状态的转换:
步骤:
使用的构造方法:
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方法
@Override
public void run() {
System.out.println("son of thread is running!");
}
}
eg:(也可以使用lambda表达式或者匿名内部类)
// 匿名内部类
new Thread(new Runnable() {
@Override
public 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()
}
}
}
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;
@Override
public 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(锁对象){
// 对共享数据的访问操作
}
修改后为:
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();
@Override
public 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代码块
中的内容synchronized
外面等待, B就处于同步阻塞状态synchronized
代码块, A释放锁synchronized
代码块中的内容.eg:
public class Demo {
// 定义一把锁
public static final Object obj = new Object();
public static void main(String[] args) {
// 定义线程A
new Thread(() -> {
synchronized (obj) {
System.out.println("进入线程A");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出线程A");
}
},"A").start();
// main休眠1s
try {
System.out.println("main函数休眠1s");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 定义线程B
new 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) {
// 定义线程A
new 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休眠1s
try {
System.out.println("main函数休眠1s");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 定义线程B
new Thread(() -> {
System.out.println("进入线程B");
synchronized (OBJECT) {
System.out.println("B进入到了synchronized中");
}
System.out.println("退出线程B");
}, "B").start();
}
}
它是一个接口
方法:
一个可重入的互斥锁 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();
@Override
public 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
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 {
// 定义一个flag
boolean flag = true;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public 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:
@Override
public 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:
@Override
public 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;
}
@Override
public 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;
}
@Override
public void run() {
// 生产包子
while (true){
// 使用synchronized
synchronized (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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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()
, 在哪个线程中调用wait(), 导致哪个线程处于阻塞状态IllegalMonitorStateException
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();
}
}
唤醒多个等待的线程
任意Java对象都能充当锁的角色