| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 初识Java多线程 -> 正文阅读 |
|
[Java知识库]初识Java多线程 |
目录 前言:本人为CS大三学生,目前属于学习阶段(小白),有问题请大佬们指正。 一、基本概念在学习多线程前,我们首先要知道这样几个概念。 1、程序:为完成特定任务,用某种语言编写的一组指令集合。即为一段静态(没有加载到内存空间,CPU没有参与运算)的代码。 2、进程:见文知意,即为正在运行的程序。在计算机操作系统中,进程是一个可拥有资源的基本单位。 3、线程:一个程序内部的一条执行路径。作为调度和分派的基本单位。 对以上举个概念栗子: 当你下载了某个杀毒软件装在电脑上还未运行时,杀毒软件即为一个程序,点击运行后系统为其分配运行需要的资源,此时即为进程。你可以用它同时进行杀毒、清理垃圾...,此时杀毒占用一条线程,清理垃圾占用一条线程。 二、线程的创建和使用方式一? 继承Thread类步骤: 1、继承Thread类 2、重写run()方法,run()方法中写多线程的代码 3、创建继承Thread类的子类对象 4、通过此对象调用start()方法 ????????说明 :start()方法的作用,启动当前线程,调用当前线程的run(); 例:主线程打印0-100的奇数,副线程打印0-100的偶数
此方式创建多线程需要注意:对于多个线程操作的数据要用static关键字修饰,使其只有一份。 方式二? 实现Runnable接口步骤: 1、创建实现Runnable接口的类 2、实现Runnable接口的类实现run()方法 3、创建实现Runnable接口类的对象 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 5、通过Thread类的对象调用start() 例:
上面我们说到?start()方法的作用是启动当前线程,调用当前线程的run(); 此时当前Thread的run()并没有实现,是怎样调用的呢? 我们可以看到JDK中Thread的一个构造方法,将Runnable接口对象传入。 Thread类中的run()方法判断(Runnable)?target是否为空,不为空则调用Runnable的run() 方式三? 实现Callable接口官方文档中Callable接口实现多线程比Runnable更为强大。 原因 · call()可以有返回值 · call()可以抛出异常 · Callable支持泛型 Callable实现多线程 步骤 1、实现Callable接口 2、重写call方法 3、创建Callable接口实现类的对象 4、将Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象 5、将FutureTask对象作为参数传递到Thread类的构造器中,创建Thread对象 6、调用start() [ 可选 7、用get()方法获取call()方法的返回值 ]
ps:我并不能理解Callable接口的方式。仅仅停留于知道它启动多线程的步骤。 方式四? 线程池先说一下线程池的优势 · 降低资源消耗。重复利用已创建的线程降低创建线程所带来的消耗 ·?提高响应速度。不需要等待线程创建即拿即用。 ·?提高线程的可管理性。线程资源不进行管理的情况下,若无限制创建会加大系统资源的消耗,且降低系统的稳定性,线程池可以对线程进行统一的管理,分配和调优。 步骤 1、创建线程池 2、创建实现Runnable接口或Callable接口的类并从写它们的抽象方法 3、用线程池对象调用execute(Runnable runnable )或submit(Callable callable) 4、不用线程池后,关闭线程池。
Executors中提供创建线程池的方式 ?newFixedThreadPool ????????创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 newCachedThreadPool ????????创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。 ... 三、线程同步问题什么是线程安全问题? ????????多个线程同时操作共享数据(临界资源)时引起的混乱结果; ps:测试时我们可以用sleep()方法模拟网络延迟,放大问题的发生性; synchronized解决线程安全问题1、同步代码块 synchronized(同步监视器){ } 说明 >操作共享数据的代码,即为需要被同步的代码 >共享数据:多个线程共同操作的变量。 >同步监视器,俗称:锁。任何一个类的对象都可以充当。? ?? ? ? ? ? 同步监视器ps:{多个线程必须共用一把锁,即共同的是同一个同步监视器。否则无效} 例:三个线程买票 实现Runnable的方式
?????????这里的同步监视器用this,此时的this即为window1,此时thread1、thread2、thread3三个线程公用的都是同一个同步监视器,可以达到同步效果。 ????????注释部分用Object的对象充当同步监视器也可以达到通用的效果。通常情况,在实现Runnable接口的方法中直接使用this充当同步监视器,不需要新建对象浪费资源。 继承Thread类的方式
????????继承Thread类的例子用Window2.class本类充当同步监视器,即为Window2。如果我们将Window2.class换成this之后,每个线程对象进入后将自己作为同步监视器,三个线程的同步监视器都不同,此时仍然会发生重票和错票,线程不安全。 ? ? ? ? PS:在使用同步代码块时,特别需要注意的除同步监视器外,还需要注意同步代码块的范围,不能多,也不能少哦。上述栗子中,如果将同步代码块包住while循环将导致第一个进入的线程将所有票全部抢完。这显然不是我们希望看到的结果。 ????????前文创建多线程的方式一(继承Thread类)中,我们曾说,共享的数据要使用static修饰,使其只有一份,此处的ticket即为共享,若去掉static,每个线程对象创建时都会有自己的ticket。最后自己拿到自己创建的无效票。实现Runnable接口的方式则无需担心此问题,Runnable接口实现类的对象只创建一次便可给多个Thread使用。 2、同步方法 说同步方法之前我们先带着一个问题,synchronized与static?synchronized的区别。 如果操作共享数据的代码完整的声明在某个方法中,只要对这个方法使用synchronized关键字修饰 ?[public] [static] synchronized void method( ){?} 同步方法中看似没有同步监视器,实则同步方法中仍然涉及同步监视器,只是我们不用显示的对其进行声明。 (万年不变的买票例子!!!) 实现Runnable接口中的同步方法
继承Thread类中的同步方法
????????细心的朋友已经发现了,实现Runnable接口的方式的同步方法是synchronized修饰,继承Thread类的方式是static?synchronized。 ????????前文的同步代码块中Runnable接口的方式使用synchronized(this),Thread中则使用synchronized( Window2.class)。当我们把static synchronized中的static去掉发现,依然会发生从票和错票现象,线程不安全。 ????????上述栗子中实现Runnable接口的方式,Runnable接口实现类的对象仍然只有一个,其余线程共用此对象实现多线程,同步方法直接使用synchronized修饰,此时的同步监视器使用即为this。而继承Thread类的方式则有多个对象,同步方法使用static synchronized修饰,同步监视器即为 XXX.class。 非静态同步方法(synchronized修饰),同步监视器是:this ?静态同步方法(static synchronized修饰),同步监视器是:当前类本身 Lock解决线程安全问题Lock(锁)解决线程安全问题的方式是JDK5.0新增的方式。 Lock接口的实现类有 ReentrantLock ReetrantReadWriteLock.WriteLock ReentrantReadWriteLock.ReadLock? 此处用ReentrantLock举例
需要注意的是:lock()必须紧跟try使用unlock() 必须在finally第一行调用。 阿里巴巴开发手册中对Lock使用的的描述 {在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。 四、线程的生命周期线程的状态 ?????????·新建? 当一个Thread类活其子类的对象被声明并创建时,新生的线程对象处于新建状态 ?????????·就绪? 调用start() 后,将进入线程队列等待CPU时间片,具备了运行的条件,只是没分配到资源 ????????·运行? 当就绪的线程被调度,并获得CPU资源时,进入运行状态,run()定义了线程的操作和功能 ????????·阻塞? 在某些特殊情况下,被挂起或执行输入输出操作时,让出CPU资源???????? ????????·死亡? 线程完成了全部工作或线程被提前强制性的终止,或出现异常导致结束 五、多线程中常用方法Thread中多线程的常用方法1、start(); 启动当前线程,调用当前线程的run()方法。 2、run():执行多线程的代码; 3、getName():获取当前线程的名称; 4、setName(String str):设置线程的名称; 5、yield():线程执行此方法的时候,释放当前cpu执行权 6、join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,直至线程b完全执行完以后,线程a才结束阻塞状态,个人理解为,操作系统中的抢占式调度。 7、sleep(long time):使当前线程阻塞 x ms(毫秒) ? ? ? ? 需要注意的是:sleep的异常不能在run()中抛出,Runnable接口中的run()方法并未抛出异常。 ?????????????????????????????????遵循子类重写规则。 Object中多线程的常用方法wait(); 执行此方法,当前线程进入阻塞状态,并释放同步监视器。 notify();执行此方法,唤醒一个阻塞的线程,如果有多个wait()的线程,唤醒优先级的高的线程。 notifyAll();执行此方法,唤醒所有wait()的线程。 PS:(1)以上三个方法必须使用在同步代码块或同步方法中 (2)以上三个方法的调用者必须是同步代码块或同步方法中的同步监视器。?否则会出IllegalMonitorStateException异常 (3)以上三个方法的定义是在Object类中。WHY? ????????原因:因为三个方法的调用者是同步监视器,又因为任意对象都可以充当同步监视器,?? ??? ??? ??? 故将这三个方法的定义放到Object中。 sleep();和wait();的异同 1、相同点:一旦执行,都可以使得当前线程进入阻塞状态。 2、不同点:1)两个方法声明的位置不同。 sleep()定义在Thread中 而。?2)调用的要求不同:sleep()可以在任何需要的场景中调用,wait()必须使用在同步代码块或同步方法中。3)关于释放同步监视器:如果sleep()在不会释放,wait()会释放。 线程的优先级MAX_PRIORIYT 10 MIN_PRIORIYT 1 NORM_PRIORIYT 5 设置线程优先级 setPriority( int p) 说明:高优先级的线程才能够要抢占低优先级cpu的执行权,但是知识从概率上讲,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 19:15:03- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |