多线程基础详解(看到就是赚到)

这篇具有很好参考价值的文章主要介绍了多线程基础详解(看到就是赚到)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

多线程基础详解(看到就是赚到),java,开发语言

  • 🎥 个人主页:Dikz12
  • 📕格言:那些在暗处执拗生长的花,终有一日会馥郁传香
  • 欢迎大家👍点赞✍评论⭐收藏

目录

 创建线程

 1.创建类继承Thread,重写run()

 2.实现Runnable,重写run()

3.继承Thread,使用匿名内部类

 4.使用lambda表达式(推荐)

线程启动 

线程中断

1.手动设置标志位

2.使用内部自带的标志位(interrupt)

线程等待 

线程状态 

线程安全

 synchronized(可重入锁) 使用方法

 死锁

关于死锁问题 

死锁能产生,一定涉及到四个必要条件 

volatile关键字(解决内存可见性问题) 


 创建线程

 1.创建类继承Thread,重写run()

class MyThread extends Thread {
    @Override
    public void run() {
        //这个就是线程的入口方法
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread t = new MyThread(); //向上转型
        t.start();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 2.实现Runnable,重写run()

class  MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

3.继承Thread,使用匿名内部类

  /* //实现了Runnable,匿名内部类的写法
      Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });*/
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

 4.使用lambda表达式(推荐)

 public static void main(String[] args) {
        //lambda 表达式 本质上是一个匿名函数,用来实现回调函数
        Thread t = new Thread(() ->{
            while (true) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

不单单只有上诉这几种,还有其它方式就不在演示了! 

线程启动 

线程启动是通过start(). 而不是run().

run():    只是单纯的描述了当前线程要执行的内容.

start() : 才是真的会调用 系统api,在系统内核上创建线程.

线程中断

1.手动设置标志位

    private static boolean isQuit = false; //成员变量
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("线程开始");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程结束");
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
        System.out.println("设置 isQuit 为 true");
    }

 要注意的是:这里用的是lambda表达式的写法,会发生变量捕获,自动捕获上层域涉及的局部变量.

是有前提限制的,就是只能捕获一个要保证是实际上的final 变量.

2.使用内部自带的标志位(interrupt)

 public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (!Thread.currentThread().isInterrupted()) {
               System.out.println("线程工作中");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
                   //1.什么都不加, 假装没听见; 继续执行
                   //2.加上break, 表示线程立即结束
                   //break;
                   //3. 可以做一些其它工作,(代码放到这里)执行完之后,在结束
                   break;
               }
           }
            System.out.println("线程结束");
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
    }

线程等待 

 让一个线程,等待另一个线程执行结束,然后在执行.   本质上就可以理解为控制线程的结束顺序.

join() -> 个哪线程调用,哪个线程就阻塞等待.

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1 结束");
        });
        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 结束");
        });
        t1.start();
        t2.start();
        System.out.println("主线程结束!");
    }

多线程基础详解(看到就是赚到),java,开发语言 

线程状态 

 在Java中,又给线程赋予了一些其它的状态.比如:

NEW: Thread对象已经创建好了,到时start()还没调用.

TERMINATED : Thread对象还在,内核中的线程已经被销毁了.

RUNNABLE: (就绪状态) 线程已经在cpu上执行了/正在排队等待cpu执行.

WAITING(阻塞): 由于wait()引起的阻塞.

TIMED_WAITING: 由于sleep() 引起的阻塞. 

BLOCKED: 由于锁竞争导致的阻塞.

线程状态在调试的时候,可以使用jdk文件下bin目录中的多线程基础详解(看到就是赚到),java,开发语言查看线程状态. 

线程安全

 想要解决线程安全问题,就要先了解产生线程不安全的原因.

1.在操作系统中,线程的调度顺序是随机的.(这是由系统内核决定的,除非换个系统)

2.两个线程,对一个变量进行修改

3.修改操作不是原子性的

4.内存可见性问题

5.指令重排序问题

比如:  针对一个变量进行修改.

 private static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(() -> {
           for (int i = 0; i < 50000; i++) {
               //加锁
               synchronized (locker1) {
                   count++;
               }
           }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                synchronized(locker1) {
                    count++;
                }
            }
        });
        t1.start();
        t2.start();
        //如果没有这俩 join, 肯定不行的. 线程还没自增完, 就开始打印了. 
        t1.join();
        t2.join();
        //预期结果应该是10W
        System.out.println(count);
    }

 这里引入锁synchronized(可重入锁),作用就是把count 这个变量,成为 原子的, 也就是降低了并发程度.

 synchronized(可重入锁) 使用方法

1.搭配代码块使用

多线程基础详解(看到就是赚到),java,开发语言 

2.搭配实例方法或者静态方法

    public int count;
    public void increase () {
        synchronized (this) {
            count++;
        }
    }
    //简化版
    synchronized  public void increase2() {
        count++;
    }
    //静态方法
    public static  void incresae3() {
        synchronized (Fun.class) {

        }
    }
    synchronized public static void increase4() {

    }

 死锁

关于死锁问题 

1.一个线程,针对 同一把锁,连续加锁,如果不是可重入锁,就会发生死锁.(Java中的synchronized是可重入的;C++的std::mutex 就是不可重入锁).

2.两个线程,两把锁.

   线程t1,得到一把锁A 后,又尝试获取锁B;   线程t2 ,得到一把锁B后,有尝试获取锁A.

    public static Object locker1 = new Object();
    public static Object locker2 = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
           synchronized (locker1) {
               // 加上sleep 为了t1 和 t2 线程都能获得一把锁
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (locker2) {
                   System.out.println("t1 加锁成功!");
               }
           }
        });
        Thread t2 = new Thread(() -> {
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1) {
                    System.out.println("t1 加锁成功!");
                }
            }
        });
        t1.start();
        t2.start();
    }

 通过调试可以看到,这两个线程进入了BLOCKED的状态(死锁)

多线程基础详解(看到就是赚到),java,开发语言

多线程基础详解(看到就是赚到),java,开发语言

这种情况是可以避免的,调整代码结构,上述代码两个synchronized 是嵌套关系,不是并列关系.

多线程基础详解(看到就是赚到),java,开发语言

3.N 个线程,M把锁.

    典型的例子: 操作系统中的 科学家就餐问题.(就不在详细讨论了) 

死锁能产生,一定涉及到四个必要条件 

1. 互斥使用(锁的基本特性): 一个线程得到一把锁之后,另一个线程也想得到这把锁,就要阻塞等待.

2.不可抢占(锁的基本特性): 一把锁已经被一个线程得到后,另一个线程只能等该线程主动释放,不能强行抢占.

3.请求保持 : 一个线程想获取多把锁(例子:死锁问题的第二个).

4.循环等待/ 环路等待: 线程之间的等待关系成环了. (例子:科学家就餐问题)

所以,解决死锁问题,只要破坏上述四个条件中的其中一个就可以.

1和2,是锁的基本特性,是破坏不了的,也就破坏这两个中的其中一个.

破坏3 : 只需要调整代码结结构,避免出现"嵌套" 逻辑.

破坏4: 约定加锁的顺序,就可以避免循环等待.

多线程基础详解(看到就是赚到),java,开发语言

volatile关键字(解决内存可见性问题) 

    private static int isQuit = 0;
    public static void main(String[] args) {
        Thread t1 = new Thread(() ->  {
           while(isQuit == 0) {
               //循环体
           }
            System.out.println("t1 退出");
        });
        t1.start();
        Thread t2 = new Thread(() ->  {
            System.out.println("请输入isQuit的值:");
            Scanner scanner = new Scanner(System.in);
             isQuit = scanner.nextInt();
        });
        t2.start();
    }

上述代码 运行效果:

多线程基础详解(看到就是赚到),java,开发语言

期望的结果是,输入1,线程结束. 而这里并没有结束.

 就需要站在cpu的分析下,整个数据的过程:

1.load 读取内存的isQuit值放到寄存器里

2.通过cmp指令比较寄存器得值是否等于0,决定是否要继续执行

读取内存的速度就已经是非常快的了,而读取寄存器的速度是 读取内存速度的 几千倍 几万倍.

所以,Java的编译器就自主做了一个大胆的决定,编译优化,只有第一次循环的时候,才读了内存,后面都是读取寄存器.

解决方案就是通过 volatile 关键字,告诉编译器不要优化!!!

 文章来源地址https://www.toymoban.com/news/detail-826771.html

 

 

 

 

 

 

 

到了这里,关于多线程基础详解(看到就是赚到)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java基础篇 | 多线程详解

    ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏: MySQL学习 🥭本文内容:Java基础篇 | 多线程详解 🖥️个人小站 :个人博客,欢迎大家访问 📚个人知识库: 知识

    2024年02月05日
    浏览(37)
  • 【Java 基础篇】Java多线程编程详解:线程创建、同步、线程池与性能优化

    Java是一门强大的编程语言,其中最引人注目的特性之一是多线程支持。多线程允许我们在同一程序中同时执行多个任务,这大大提高了应用程序的性能和响应能力。本文将深入介绍Java线程的基础知识,无论您是初学者还是有一些经验的开发人员,都将从中获益。 在计算机科

    2024年02月07日
    浏览(57)
  • Java/Python/Go不同开发语言在进程、线程和协程的设计差异

    在多线程项目开发时,最常用、最常遇到的问题是 1,线程、协程安全 2,线程、协程间的通信和控制 本文主要探讨不同开发语言go、java、python在进程、线程和协程上的设计和开发方式的异同。 进程 进程是 操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空

    2024年01月23日
    浏览(50)
  • 详解MySQL C API 相关接口(大白话就是:MySQL的c语言怎么写)

    1、C API 官方文档 关于C语言连接数据所涉及到的各种数据结构的介绍以及相关函数的使用其实在 MySQL C API 官方文档中已经给出了,我们可以通过它来快速了解并上手 MySQL C API。 2、初始化 MYSQL 要使用 MySQL C语言库,需要先使用 mysql_init 函数完成对 MYSQL 结构体指针的初始化工作

    2024年04月28日
    浏览(38)
  • 做独立开发者,能在AppStore赚到多少钱?

    成为一名独立开发者,不用朝九晚五的上班,开发自己感兴趣的产品,在AppStore里赚美金,这可能是很多程序员的梦想,今天就来盘一盘,这个梦想实现的概率有多少。 先来了解一些数据: 2022年5月26日,苹果公司披露了最新的开发者数据。截至目前,全球开发者数量突破3

    2024年02月11日
    浏览(41)
  • 【Go 基础篇】Go语言包详解:模块化开发与代码复用

    在Go语言中, 包(Package) 是一种用于组织代码的机制,用于将相关的函数、类型和变量等组织在一起,以便于模块化开发和代码复用。包的使用能够使程序结构更加清晰、可维护性更高,同时也是Go语言强调的一项重要特性。本篇博客将深入探讨Go语言中包的相关知识,包括

    2024年02月11日
    浏览(47)
  • 我用ChatGPT开发一个小程序赚到第一桶金

    ChatGPT是OpenAI基于GPT-3.5架构训练出来的一个大型语言模型,其拥有广泛的使用者群体。随着机器学习技术的不断进步,ChatGPT在人工智能应用领域中的应用越来越广泛。 ChatGPT被广泛应用于智能客服领域,因为它可以帮助企业提高客户满意度和快速响应客户需求。在电商、在线

    2024年02月07日
    浏览(45)
  • python多线程----------主线程,子线程,任务讲解----拿下就是胜利

    这一篇博客主要介绍给分不清楚主线程.子线程的小可爱们 在之前的一篇博客中我简单的介绍了并发,并行 并发:是在时段的完成多个任务 ,但是每个时间点只有一个任务运行 而多线程就是这一个原理 代码: 结果: 可以看出主线程执行完成,但是子线程还在运行,这就是非守护线程

    2024年02月10日
    浏览(37)
  • 编程开发8大语言详解,为什么Java是我最推荐的?

    很多没有接触过编程语言的同学,都会觉得编程开发特别高端和神奇,担心理解不了更担心学不会。 当然,也有人会认为,你既然是做编程的,那么你应该什么都会,什么软件的开发都能完成,这是平哥经常听到的两种声音。 在此,平哥需要给大家科普一下, 编程确实改变

    2024年02月05日
    浏览(73)
  • 【Windows线程开发】线程基础

    本篇文章来带领大家了解Windows线程,了解线程的基本概念,了解线程的创建方式,以及一些简单的线程操作。 Windows线程是可以执行的代码的实例,系统是以线程为单位调度程序。一个程序中可以有多个线程,实现多任务的处理。 Windows线程的特点: 每个线程都具有一个ID 每

    2024年02月04日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包