Juc01_多线程概述、四种实现方式、常用方法API、生命周期、买票案例、synchronized锁(一)

简介: ①. 多线程的概述(面试高频问点)②. 多线程的实现方式①. 继承Thread②. 实现Runnable接口

说明


(1). 关于synchronized关键字底层原理参考如下文章


(2). Juc18_Java内存模型、对象头Mark Word、实例数据、对齐填充、谈谈new Object( )占多大内存


(3). Juc19_从字节码角度看synchronize、Monitor类、monitorenter、monitorexit


(4). Juc20_Synchronized锁升级、无锁、偏向锁、轻量级锁、重量级锁、锁消除、锁粗化


①. 多线程的概述(面试高频问点)


  • ①. 为什么使用多线程及其重要


摩尔定律失效(硬件方面):


(1). 集成电路上可以容纳的晶体管数目在大约每经过18个月便会增加一倍,可是从2003年开始CPU主频已经不再翻倍,而是采用多核而不是更快的主频


(2). 在主频不再提高且核数不断增加的情况下,要想让程序更快就要用到并行或并发编程

高并发系统,异步+回调的生产需求(软件方面)


②. 进程、线程、管程(monitor 监视器)


线程就是程序执行的一条路径,一个进程中可以包含多条线程


多线程并发执行可以提高程序的效率,可以同时完成多项工作


举例:


[你打开一个word就是一个进程开启了,这个时候你重启后在打开word,这个时候有一个点击恢复的按钮,这就是一个线程,可能这个线程你看不到,你打字的时候,单词打错了,word中会有一个波浪线,这也是一个线程]


管程:Monitor(监视器),也就是我们平时所说的锁


(Monitor其实是一种同步机制,它的义务是保证(在同一时间)只有一个线程可以访问被保护的数据和代码)


JVM中同步时基于进入和退出的监视器对象(Monitor,管程),每个对象实例都有一个Monitor对象。


Monitor对象和JVM对象一起销毁,底层由C来实现


③. 多线程并行和并发的区别


并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行(需要多核CPU)


并发是指两个任务都请求运行,而处理器只能接收一个任务,就是把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行(12306抢票的案例)


④. wait | sleep的区别?功能都是当前线程暂停,有什么区别?


wait放开手去睡,放开手里的锁;wait是Object类中的方法


sleep握紧手去睡,醒了手里还有锁 ;sleep是Thread中的方法


⑤. synchronized 和 lock的区别?


(1). 原始构成
 a. synchronized是关键字属于JVM层面
 monitor对象,每个java对象都自带了一个monitor,需要拿到monitor对象才能做事情
 monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖monitor对象,
 只能在同步块或方法中才能调用wait/notify等方法),进入
 monitorexit:退出
 b. lock是api层面的锁,主要使用ReentrantLock实现
(2). 使用方法
 a. synchronized不需要用户手动释放锁,当synchronized代码完成后系统会自动让线程释放
对锁的占用
 b. ReentrantLock则需要用户手动释放锁若没有主动释放锁,就有可能会导致死锁的现象
(3). 等待是否可中断?
 a. synchronized不可中断,除非抛出异常或者正常运行完成
 b. ReentrantLock可中断
 (设置超时时间tryLock(long timeout,TimeUnit unit),调用interrupt方法中断)
(4). 加锁是否公平
 a. synchronized非公平锁
 b. ReentrantLock两者都可以,默认是非公平锁,构造方法可以传入boolean值,true为公平锁,
 false为非公平锁
(5). 锁绑定多个Condition
 a.synchronized没有
 b.ReentrantLock用来实现分组唤醒需要唤醒线程们,可以精确唤醒,而不是像synchronized要么
 随机唤醒一个\要么多个


②. 多线程的实现方式


①. 继承Thread


  //注意:打印出来的结果会交替执行
  public class ThreadDemo{
      public static void main(String[] args) {
          //4.创建Thread类的子类对象
          MyThread myThread=new MyThread();
          //5.调用start()方法开启线程
          //[ 会自动调用run方法这是JVM做的事情,源码看不到 ]
          myThread.start();
          for (int i = 0; i < 100; i++) {
              System.out.println("我是主线程"+i);
          }
      }
  }
  class MyThread extends Thread{
      //2.重写run方法
      public void run(){
          //3.将要执行的代码写在run方法中
         for(int i=0;i<100;i++){
             System.out.println("我是线程"+i);
         }
      }
  }


②. 实现Runnable接口


  • ①. 源码分析如下:


微信图片_20220106173408.png


public class RunnableDemo {
    public static void main(String[] args) {
        //4.创建Runnable的子类对象
        MyRunnale mr=new MyRunnale(); 
        //5.将子类对象当做参数传递给Thread的构造函数,并开启线程
        //MyRunnale taget=mr; 多态
        new Thread(mr).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("我是主线程"+i);
        }
    }
}
//1.定义一个类实现Runnable
class MyRunnale implements Runnable{
    //2.重写run方法
    @Override
    public void run() {
        //3.将要执行的代码写在run方法中
        for (int i = 0; i < 1000; i++) {
            System.out.println("我是线程"+i);
        }
    }
}


②. 两种实现多线程方式的区别


  (1).查看源码
  a.继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()
  方法
    b.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用
 run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),
 运行时执行的是子类的run()方法
    (2).继承Thread
    a.好处是:可以直接使用Thread类中的方法,代码简单
    b.弊端是:如果已经有了父类,就不能用这种方法
    (3).实现Runnable接口
    a.好处是:即使自己定义的线程类有了父类也没有关系,因为有了父类可以实现接口,而且接口
可以多现实的
    b.弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,
代码复杂


相关文章
|
15天前
|
JSON 缓存 API
淘系商品详情API接口概述,API文档说明
在成长的路上,我们都是同行者。这篇关于API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦! 淘宝API接口文档是淘宝开放平台为开发者提供的一套详细的技术规范和使用指南,旨在帮助开发者通过API接口与淘宝平台进行交互,获取商品详情等数据。以下是对淘宝商品详情数据解析的详细说明:
|
1月前
|
Java 调度
|
17天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
1月前
|
存储 Java 调度
线程池的概述和创建
线程池的创建,构造器需要分别传入什么参数
线程池的概述和创建
|
24天前
|
算法 安全 Java
三种方法教你实现多线程交替打印ABC,干货满满!
本文介绍了多线程编程中的经典问题——多线程交替打印ABC。通过三种方法实现:使用`wait()`和`notify()`、`ReentrantLock`与`Condition`、以及`Semaphore`。每种方法详细讲解了实现步骤和代码示例,帮助读者理解和掌握线程间的同步与互斥,有效解决并发问题。适合不同层次的开发者学习参考。
42 11
|
18天前
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
41 3
|
29天前
|
安全 Java API
|
1月前
|
Java
java开启线程的四种方法
这篇文章介绍了Java中开启线程的四种方法,包括继承Thread类、实现Runnable接口、实现Callable接口和创建线程池,每种方法都提供了代码实现和测试结果。
java开启线程的四种方法
|
1月前
|
存储 算法 Oracle
19 Java8概述(Java8概述+lambda表达式+函数式接口+方法引用+Stream+新时间API)
19 Java8概述(Java8概述+lambda表达式+函数式接口+方法引用+Stream+新时间API)
55 8
【多线程面试题 二】、 说说Thread类的常用方法
Thread类的常用方法包括构造方法(如Thread()、Thread(Runnable target)等)、静态方法(如currentThread()、sleep(long millis)、yield()等)和实例方法(如getId()、getName()、interrupt()、join()等),用于线程的创建、控制和管理。