java基础13--线程

线程

线程的相关概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

线程的其他概念

并行:指两个或多个事件在同一时刻点发生;
并发:指两个或多个事件在同一时间段内发生(交替进行)
在这里插入图片描述

线程的使用

创建线程的两种方式

在这里插入图片描述

继承Thread类

如果主线程先结束了,子线程还没结束,不代表程序的进程结束了
在这里插入图片描述

public class ThreadUse {
    @Override
    public void run() {
        super.run();
    }

    public static void main(String[] args) throws InterruptedException {
        Cat cat = new Cat();
        cat.start();//启动线程
        //说明:当main线程启动一个子线程Thread-0,主线程不会阻塞.会继续执行
        //这时主线程和子线程会交替执行
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            Thread.sleep(1000);

        }
    }
}

//1.当一个类继承了Thread该类就可以作为一个线程类了
//2.我们会重写run方法,写上自己的业务代码
//3.run Thread类实现类Runnable接口的run方法
class Cat extends Thread {
    int times = 0;

    @Override
    public void run() {//重写run方法,写上自己的逻辑
        while (times < 10) {
            System.out.println("我是小喵喵" + (++times) + "线程名" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
run方法和start方法的区别

1、线程中的start()方法和run()方法的主要区别在于,当程序调用start()方法,将会创建一个新线程去执行run()方法中的代码。但是如果直接调用run()方法的话,会直接在当前线程中执行run()中的代码,注意,这里不会创建新线程。这样run()就像一个普通方法一样。

2、另外当一个线程启动之后,不能重复调用start(),否则会报IllegalStateException异常。但是可以重复调用run()方法。

总结起来就是run()就是一个普通的方法,而start()会创建一个新线程去执行run()的代码。

源码解读

真正实现多线程效果的是本地的start0方法而不是run方法
在这里插入图片描述

在这里插入图片描述

实现Runnable接口

java是单继承的,所以想要创建线程只能通过实现Runnable接口来实现
在这里插入图片描述
在这里插入图片描述

具体实现方法(静态代理方式)

在Thread实例中传入要创建线程的对象

public class ThreadUse02 {

    public static void main(String[] args) {
        Dog dog = new Dog();
        //dog.start();这里不能调用start,因为没有这个方法
        //只能创建Thread对象,把dog对象(实现了Runnable),放入Thread
        Thread thread = new Thread(dog);
        //然后调用thread的start方法
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

class Dog implements Runnable {
    int times = 0;

    @Override
    public void run() {
        while (times < 10) {
            System.out.println("我是汪汪" + (++times));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
模拟静态代理方式

要点:
1. 代理类和被代理类要实现同一个接口
2. 代理的构造方法中传入被代理对象
3. 代理调用接口中的某个方法,但实际的运行类型是被代理的类型(这边start和start0是为了模拟底层实现)

//线程代理类,模拟了极简的的Thread类
class ThreadProxy implements Runnable {
    //定义了Runnable类型的target
    private Runnable target = null;

    @Override
    public void run() {
        if (target != null) {
            target.run();//动态绑定(运行类型是传入代理的实参的运行类型,也就是Tiger类型,然后会调用tiger的run方法)
        }
    }

    //把形参赋给实参
    public ThreadProxy(Runnable target) {
        this.target = target;
    }

    //代理实例化后,使用代理的start()方法来对用本地的start0方法,该方法会对用run方法
    public void start() {
        start0();
    }

    public void start0() {
        run();
    }
}

class Animal {
}

//java中单继承,所以继承了Animal就不能继承Thread类了,所以只能实现Runnable接口
class Tiger extends Animal implements Runnable {
    int count = 0;

    @Override
    public void run() {
        System.out.println("老虎在咆哮" + (++count));
    }
}


多线程执行

public class MulThread02 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();
        thread2.start();
    }
}

class T1 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        while (count < 5) {
            System.out.println("hi" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class T2 implements Runnable {
    int count = 0;

    @Override
    public void run() {
        while (count < 10) {
            System.out.println("hello world" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

继承Thread和实现Runnable的区别

在这里插入图片描述

多线程售票问题

在这里插入图片描述
出现了超卖问题
在这里插入图片描述

线程终止

两种方式

在这里插入图片描述
这边的通知方式其实就是设计一个标志

public class quit {

    public static void main(String[] args) {
        T t = new T();
        t.start();
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.setLoop(false);

    }
}

class T extends Thread {
    int i = 0;
    boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(++i);
        }
    }
}

线程常用方法

注意:

  • 继承了Thread类才有的
  • 优先级只是说概率变大了,并不一定按照优先级顺序执行
  • interrupt()方法只是改变中断状态,不会中断一个正在运行的线程,它会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。(可以利用使用interrupt方法会修改isInterrupted状态位来中断循环,或者使用interrupt方法中断sleep时会抛出异常来中断进程)

在这里插入图片描述

public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
  
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("拿出一瓶牛奶!");
            }
        });
  
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("关上冰箱!");
            }
        });
  
        //下面三行代码顺序可随意调整,程序运行结果不受影响,因为我们在子线程中通过“join()方法”已经指定了运行顺序。
        thread3.start();
        thread2.start();
        thread1.start();
  
    }
}

    2.在主线程中通过join()方法指定顺序

public class ThreadMainJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("打开冰箱!");
            }
        });
  
        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("拿出一瓶牛奶!");
            }
        });
  
        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("关上冰箱!");
            }
        });
  
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }
}

在这里插入图片描述
注意:

  • yield不一定礼让成功
  • join一定成功,先start后join
  • 可以通过join指定线程的执行顺序
    1.在子线程中通过join()方法指定顺序

注意事项和细节

在这里插入图片描述

用户线程和守护线程

在这里插入图片描述
在这里插入图片描述

用法

daemon.setDaemon(true);
注意: 该方法在start方法前,否则报异常

线程的生命周期

在这里插入图片描述
在这里插入图片描述

线程同步

线程同步机制

在这里插入图片描述

线程同步的方法

Synchronized关键字

在这里插入图片描述

线程同步的原理

始终只能有一个线程去“开锁”,操作数据
在这里插入图片描述

互斥锁

售票问题(有助于充分理解线程)

注意事项:

  1. 要知道当前到底有几个线程
  2. 要知道当前的票是属于线程对象的还是属于线程类变量的
package com.bijing.thread.ticket;

/**
 * @author 毕晶
 * @date 2022/6/18 8:22 PM
 */
public class SellTicket {
    public static void main(String[] args) throws InterruptedException {
        //三个线程对象,只能通过static来实现资源"票"共享,锁的是类对象,资源也是类变量
        //类变量num=100;
//        T1 t1 = new T1();
//        T1 t2 = new T1();
//        T1 t3 = new T1();
//        t1.start();
//        t2.start();
//        t3.start();

//        三个线程对象,但是共享了同一个线程资源(类变量的),有static,锁定还是类对象
//        T2 t1 = new T2();
//        T2 t2 = new T2();
//        T2 t3 = new T2();
//        new Thread(t1).start();
//        new Thread(t2).start();
//        new Thread(t3).start();

//       同一份资源"票"被三个线程共享,无static,锁的是当前获得资源的线程对象,一个厕所三个人用,谁用谁上锁
        T3 t3 = new T3();
        new Thread(t3).start();
        new Thread(t3).start();
        new Thread(t3).start();

//        三份资源"票"被三个线程共享,无static,锁的是当前获得资源的线程对象,三个厕所三个人用,谁用谁上锁
//        T4 t1 = new T4();
//        T4 t2 = new T4();
//        T4 t3 = new T4();
//        new Thread(t1).start();
//        new Thread(t2).start();
//        new Thread(t3).start();

    }
}

class T1 extends Thread {
    public static int num = 500;
    public static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            sell();
        }
    }

    public synchronized static void sell() {
        if (num > 0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, 剩余票数=" + (--num));
        } else {
            System.out.println("售票结束");
            flag = false;
            return;
        }
    }
}

class T2 implements Runnable {
    public static int num = 500;
    public static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            sell();
        }
    }

    public synchronized static void sell() {
        if (num > 0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, 剩余票数=" + (--num));
        } else {
            System.out.println("售票结束");
            flag = false;
            return;
        }
    }
}

class T3 implements Runnable {
    public int num = 500;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (num > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, 剩余票数=" + (--num));
                } else {
                    System.out.println("售票结束");
                    break;
                }
            }
        }

    }
}

class T4 implements Runnable {
    public int num = 100;

    @Override
    public void run() {
        synchronized (this) {
            while (true) {
                if (num > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, 剩余票数=" + (--num));
                } else {
                    System.out.println("售票结束");
                    break;
                }
            }
        }

    }
}

线程死锁

在这里插入图片描述

释放锁

在这里插入图片描述

不释放锁

在这里插入图片描述

作业

在这里插入图片描述
分析:

  1. 一个类访问另一个类的数据,要么用static,要么实例化一个类,这边显示实例化更好
  2. 在随机打印过程中要读取Q,这显然是并发的,所以要使用多线程,如果就单线程的话会出现打印动作不结束,读取Q就不进行
public class Homework01 {

    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2(t1);
//        t1.setDaemon(true);  或者不用setLoop,用守护线程,把t1设为守护线程,t2完成后t1自动结束
        t1.start();
        t2.start();
    }
}

class T1 extends Thread {
    private boolean loop = true;

    @Override
    public void run() {
        while (loop) {
            System.out.println((int) (Math.random() * 100 + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

class T2 extends Thread {
    Scanner scanner = new Scanner(System.in);
    String input;
    T1 t1;
    
    // 确保t2操作的是同一个t1
    public T2(T1 t1) {
        this.t1 = t1;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("请输入:");
            input = scanner.next();
            if (input.contains("Q")) {
                t1.setLoop(false);
                break;
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值