线程的学习笔记

素颜马尾好姑娘i 2022-04-23 22:42 230阅读 0赞

线程和进程的区别

  • 线程是资源调用的最低单位
  • 进程是cpu 调用的最低单位
  • 一个进程至少一个的线程

线程的两种创建方式以及优缺点:

  1. 继承 *Thread*的方法

    class Test extends Thread{

    1. public void run(){
    2. // 重写run方法
    3. }

    }

    public static void main(String[] arg){

    1. Test t=new Test();
    2. t.start();

    }

  2. 实现 Runnble 接口

    class Test implements Runnable{
    // 重写run方法
    public void run(){

    }
    public static void main(String[] arg){

    1. Test t=new Test();
    2. Thread t=new Thread(t).start():

    }

    }

  3. 使用Callable和Future创建线程

  4. 使用线程池的方式创建线程
  5. 两种方式的优缺点

    • 继承的优点

      • 可以直接调用Thread 的父类方法
      • 创建简单 使用简单
      • 如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程
    • 缺点

      • java 是单继承的机制,继承Thread 的话不能继承其他
    • 实现接口的优点

      • 只是实现了Runable接口,还可以继承其他的类
      • 可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想
    • 缺点

      • 编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法

注:java中推荐使用接口方式实现线程

线程的五种状态

  • 创建 new :当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

  • 可运行(就绪) Runnable :当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

  • 运行 Running :当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 阻塞 Blocked :处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

    • 等待式阻塞 :运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
    • 同步式阻塞 :线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态
    • 其他阻塞 :通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
  • 终止 Dead:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程的同步方式

线程安全原因:

java中在使用多线程加快运行效率的同时因为因为cpu的使用机制问题在多个线程访问同一个资源的同时会可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错所以就产生线程安全问题

解决方式

  1. 使用同步代码块

    Object o=new Object();
    synchronized (o){

    1. // 需要同步的线程代码
    2. }

    }

  2. 使用同步方法

    public synchronized void Test2(){

    }

  3. Lock 方式

    public void Test2(){

    1. Lock lock=new ReentrantLock();
    2. lock.lock();//开启同步
    3. lock.unlock();//关闭同步

    }

线程的生命周期

Au1wYd.jpg

线程之间的通信

什么是线程之间的通信:

  • 多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。
  • wait()方法

    • 在线程中调用的notfiy() 和notfiyAll()之前一直保持沉睡状态导致当前线程停止
    • 调用wait()方法会释放锁对象然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
    • 要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
  • notfiy()

    • 如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)
    • notify()方法应该是被拥有对象的锁的线程所调用

wait/notfiy机制
用创建者和消费者来简单的演示下线程的通信

1.创建包子对象(也是个锁对象)

  1. public class baozi {
  2. private int num;
  3. public baozi(int num) {
  4. this.num = num;
  5. }
  6. public int getNum() {
  7. return num;
  8. }
  9. public void setNum(int num) {
  10. this.num = num;
  11. }
  12. public void add() {
  13. num++;
  14. }
  15. public void remove() {
  16. num--;
  17. }
  18. }

2.创建消费者对象

  1. public class customer implements Runnable {
  2. baozi baozi;
  3. public customer(baozi baozi) {
  4. this.baozi = baozi;
  5. }
  6. @Override
  7. public void run() {
  8. while (true) {
  9. synchronized (baozi) {
  10. if (baozi.getNum() == 0) {
  11. try {
  12. baozi.wait();//当包子为零的时候停止消费包子
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. } else {
  17. baozi.remove();//当包子不为零的时候就开始消费包子
  18. System.out.println("包子还剩" + baozi.getSize() + "个");
  19. baozi.notify();//当执行完后 唤醒其他线程生产包子
  20. }
  21. }
  22. }
  23. }
  24. }

3.创建消费者对象

  1. public class customer implements Runnable {
  2. baozi baozi;
  3. public customer(baozi baozi) {
  4. this.baozi = baozi;
  5. }
  6. @Override
  7. public void run() {
  8. while (true) {
  9. synchronized (baozi) {
  10. if (baozi.getNum() == 0) {
  11. try {
  12. baozi.wait();//当包子为零的时候停止消费包子
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. } else {
  17. baozi.remove();//当包子不为零的时候就开始消费包子
  18. System.out.println("包子还剩" + baozi.getSize() + "个");
  19. baozi.notify();//当执行完后 唤醒其他线程生产包子
  20. }
  21. }
  22. }
  23. }
  24. }

4.创建测试类

  1. package com.zerolg.ThreadDemo.Test;
  2. public class Test {
  3. public static void main(String[] args) {
  4. baozi baozi = new baozi(10);
  5. customer customer = new customer(baozi);
  6. generate generate = new generate(baozi);
  7. Thread thread = new Thread(customer);
  8. thread.start();
  9. Thread thread1 = new Thread(generate);
  10. thread1.start();
  11. }
  12. }

效果:
a74cb9c0ly1g18ebbixt8j20c60b80tm.jpg

如上所示,案例表明生产者消费者之间就是运用了wait()和notify()这两个方法,通过通信的沉睡与唤醒机制来完成两个不同线程操作统一数据之间的通信

发表评论

表情:
评论列表 (有 0 条评论,230人围观)

还没有评论,来说两句吧...

相关阅读

    相关 学习笔记——OS线

    一、线程的引入 多处理机OS中 进程回顾: 1)进程是一个拥有资源的独立单位; 2)是一个可独立调度和分派的基本单位。 进程一系列操作: 1)创建进程:分配除C

    相关 线学习笔记

    1.进程 线程 进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是执行程序的一次执行

    相关 线(IOS)学习笔记

    1、除了锁,系统还提供了条件,确保在你的应用程序任务执行的适当顺序。一个条 件作为一个看门人,阻塞给定的线程,直到它代表的条件变为真。当发生这种情况的 时候,条件释放该线程并允