IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: 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知识库]笔记——多线程

目录

多线程

同步和异步

程序,进程,线程

多线程的优势

并行,并发

线程优先级

线程优先级的获取和设置

线程调度

调度策略

线程分类

Thread类

Thread类的特点

Thread类的构造方法

Thread类的API

线程创建

JDK5.0之前的线程创建方式

方式一:继承于Thread类

方式二:实现Runnable接口

两种创建方式的异同

联系

相同点

不同点

JDK5.0新增的线程创建方式

方式三:实现Callable接口

如何理解实现Callable接口的方式创建多线程比实现Runable接口创建多线程方式强大?

联系

区别

方式四:线程池

线程池的API

线程生命周期

线程休眠

线程让步

线程串联

线程终止

线程同步

基本概念

多线程的安全问题

解决方案?

方式一:同步代码块(synchronized关键字修饰代码块)

方式二:同步方法(synchronized关键字修饰方法)

方式三:Lock(锁)

synchronized与Lock的异同

线程通信

sleep()和wait()的异同

练习:实现奇数和偶数交叉打印

练习:实现宠物:打工(消耗体力,消耗清洁度) 洗澡(增加清洁度) ?休息(增加体力)

练习:五个人买三张票


多线程

同步和异步

同步:当用户提交一个任务时,由于需要获取更多资源信息,任务不能立即完成,导致其他任务必须等待

异步:当用户提交多个任务时,每次只能执行一个任务,而当前任务由于需要获取更多资源信息,当前无法继续运行,那么保存好当前任务的状态,并切换开始下一个任务

程序,进程,线程

程序(program):用某种语言编写的,一组计算机能识别和执行的指令(一段可以保存在磁盘中的,静态的代码)。

进程(process):正在执行中的程序或程序的一次执行过程,在内存中产生该程序的实例,是一段动态的过程(资源分配的单位)。

线程(thread):一个进程中包含多个同时执行的任务,每个任务都称为一个独立的线程,是一段程序内部的一条执行路径(调度和执行的单位)。

  • 每一个线程都有独立的运行栈和程序计数器
  • 一个Java应用程序,至少存在三个线程:主线程垃圾回收线程异常处理线程,其中,垃圾回收线程和异常处理线程都是守护线程

?

多线程的优势

  • 提高应用程序的响应速度
  • 提高计算机系统CPU的利用率
  • 改善程序结构(将既长又复杂的进程分为多个线程,独立运行)

并行,并发

  • 并行:多个任务在一个CPU的多个内核或多个CUP中同时执行
  • 并发:在单位时间内,一个CPU的一个内核中执行多个任务,每个任务交叉执行

线程优先级

  • MAX_PRIORITY:10
  • NORM_PRIORITY:5(默认优先级)
  • MIN_PRIORITY:1

线程优先级的获取和设置

  • getPriority():获取线程的优先级
  • setPriority():设置线程的优先级

线程创建时会继承父线程的优先级。

概率上,高优先级的线程会抢占低优先级的线程的CPU执行权,但并不意味着只有当高优先级的线程执行完以后,低优先级的线程才能执行。

线程调度

调度策略

  • 时间片(分时调度):线程轮流获得CPU的使用权,并平均分配每个线程占用的CPU时间片
  • 抢占式(抢占式调度):高优先级的线程抢占CPU,如果线程的优先级相同,随机选择一个线程占用CPU

同优先级线程,组成队列(先进先出),使用时间片策略。

不同优先级线程,使用优先调度的抢占式策略。

线程分类

  • 守护线程:一个线程为另一个线程服务,并且随着该线程终止而停止(垃圾回收线程)
  • 用户线程

区别:JVM的退出。

如果当前的JVM中全是守护线程,那么JVM将会退出。

Thread类

Java语言的JVM通过java.lang.Thread 类来体现允许程序运行多个线程。

Thread类的特点

  • 每一个线程都是通过某个特定Thread对象的run()来完成操作,通常把run()的主体称为线程体
  • 通过Thread对象的start()启动对应的线程,而非直接调用run()

Thread类的构造方法

  • Thread():创建新的Thread对象
  • Thread(String threadname):创建线程并指定线程实例名
  • Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
  • Thread(Runnable target, String name):创建新的Thread对象

Thread类的API

  • Thread(String name):String name:线程的名字
  • Thread(Runnable target):Runnable target:实现Runable接口的类的对象
  • Thread(Runnable target, String name) :String name:线程的名字,Runnable target:实现Runable接口的类的对象
  • static Thread currentThread():获取当前线程
  • long getId():获取线程的ID
  • String getName():返回线程的名称
  • static void sleep(long millis):设置线程休眠,long millis:休眠的时间,单位:毫秒
  • void setName(String name):设置线程的名字

线程创建

JDK5.0之前的线程创建方式

方式一:继承于Thread类

  1. 创建一个继承于Thread类的子类
  2. 重写Thread类中的run(),线程任务声明于run()内
  3. 创建一个继承于Thread类的子类的对象
  4. 对象调用start()启动线程

方式二:实现Runnable接口

  1. 创建一个实现Runable接口的实现类
  2. 实现类中实现Runable接口中抽象方法run(),线程任务声明于run()内
  3. 创建实现类的对象
  4. 将实现类的对象作为参数,创建Thread类的对象
  5. Thread类的对象调用start()启动线程
  • 调用run()只是普通的方法调用
  • 线程的执行不仅仅取决于JVM,还依赖于OS,调用start()是通知JVM线程进入就绪状态,但会不会立即执行,需要JVM进一步通知OS,由OS内部来进行实现和分配

两种创建方式的异同

联系

Thread类实现了Runable接口

相同点

  • 都需要重写run()
  • 都将要执行的代码声明在run()中
  • 都需要调用Thread类中的start()来启动线程

不同点

  • 继承Thread类:线程代码存放Thread子类的run()中
  • 实现Runnable接口:线程代码存在接口的子类的run()中

二者中,建议优先选择实现Runnable接口的方式创建

理由:

  • 实现接口的方式没有类的单继承性局限
  • 采用接口能够更好的实现数据共享

JDK5.0新增的线程创建方式

方式三:实现Callable接口

  1. 创建一个实现Callable接口的实现类
  2. 线程任务声明在call()内
  3. 创建Callable接口实现类的对象
  4. 将Callable接口实现类的对象作为参数,创建FutureTask类的对象
  5. 将FutureTask类的对象作为参数,创建Thread类的对象
  6. Thread类的对象调用start()启动线程
  7. 获取call()的返回值

如何理解实现Callable接口的方式创建多线程比实现Runable接口创建多线程方式强大?

联系

FutureTask类同时实现了Runable接口和Future接口。

FutrueTask是Futrue接口的唯一的实现类。

区别

  • call()可以有返回值
  • call()可以抛出异常,被外面的操作捕获,获取异常的信息
  • Callable接口支持泛型

方式四:线程池

线程池:一个容纳多个线程的容器。

  1. 创建线程池对象
  2. 创建Runable接口实现类对象或Callable接口实现类对象
  3. 关闭线程池

线程池的API

  • corePoolSize:核心池的尺寸
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程存活时间

线程池的优点

  • 减少创建新线程的时间,提高响应速度
  • 重复利用线程池中的线程,不需要每次都创建线程,降低资源消耗
  • 便于线程的管理

线程生命周期

线程休眠

  • 将正在运行线程从运行状态直接切换休眠状态,休眠时间到之后,线程切换为就绪状态
  • static void sleep(long millis):设置线程休眠,long millis:休眠的时间,单位:毫秒? ? ? ?

线程让步

  • 当前正在运行的线程调用了yield()方法之后,切换至就绪状态,然后继续获取时间片
  • static void yield();

线程串联

  • 当主线程调用子线程的join()方法时,意味着必须等子线程执行完毕之后,主线程才会开始执行
  • void join();
  • void join(long millis);
  • void join(long millis, int nanos);

线程终止

  • 调用stop方法。暴力终止,导致线程相关资源丢失,容易造成死锁
  • 使用标识的方式终止线程
  • 使用守护线程:void setDaemon(boolean on):on为true就是当前线程的守护线程

线程同步

基本概念

  • 临界资源(共享数据):多个线程访问的同一个资源,该资源被多个线程所共享
  • 互斥锁(同步监视器):俗称“锁”,任何一个类的对象都可以充当锁,当一个线程获取锁之后,那么其他线程无法访问该资源,只能等待锁被还回,获取锁之后,才能访问该资源
  • 死锁(锁死):当多个线程访问临界资源时,由于其他原因导致获取锁的线程死亡或出现意外现象,导致锁没有还回,而其他线程又无法访问该资源

程序出现死锁现象后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续执行。

多线程的安全问题

  • 多个线程执行的不确定性引起执行结果的不稳定
  • 多个线程临界资源的共享,会造成操作的不完整性,会破坏数据

解决方案?

通过同步机制,来解决线程安全问题(即对操作临界资源的多线程,只能在其中一个线程获取临界资源并执行完毕释放后,其他线程才能获取并执行)。

方式一:同步代码块(synchronized关键字修饰代码块)

互斥锁与synchronized关键字联动。

用法:synchronized(互斥锁){
? ? ? ?//需要被同步的代码;
}

  • 同步块尽量少包含一些代码,因为同步的方式虽然解决了线程的安全问题,但在操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率较低
  • 同步块可以发生嵌套
  • 同步代码块互斥锁:自己指定,this或类名.class

方式二:同步方法(synchronized关键字修饰方法)

如果操作临界资源的代码完整声明在一个方法中,可以将此方法声明为同步的。

用法:synchronized修饰方法?
? ? ? ? ? ? ? 等价于
? ? ? ? ? synchronized(this){
? ? ? ? ? ? ? //方法体的代码块
? ? ? ? ? }?

  • 非静态的同步方法,互斥锁:this
  • 静态的同步方法,互斥锁:当前类本身

方式三:Lock(锁)

在JDK1.5以后,在并发包(java.util.concurrent)里面添加包locks,并提供Lock接口。

synchronizedsynchronizedsynchronized的锁功能类似,但不同的是Lock需要手动开启锁和释放锁。

  • java.util.concurrent.locks.Lock接口是控制多个线程对临界资源进行访问的工具
  • ReentrantLock类实现了Lock接口,此类拥有与synchronized相同的并发性和内存语义

用法:

  • 实例化ReentrantLock
  • 调用lock():锁定方法
  • 调用unlock():解锁方法

同步代码如果有异常,要将unlock()写入finally语句。

synchronized与Lock的异同

相同点

  • 都可以解决线程的安全问题

不同点

  • synchronized是关键字,Lock是接口
  • synchronized有代码块锁和方法锁,Lock只有代码块锁
  • synchronized机制在执行完相应的同步代码块或同步方法后,会自动释放锁,而Lock需要手动的启动同步(lock()),结束同步也需要手动释放(unlock())

线程通信

wait(),notify(),notifyAll()都定义在java.lang.Object类中,都必须使用在synchronized代码块或synchronized方法中。

  • wait():线程释放对象锁 进入阻塞等待池中
  • notify():将阻塞等待池中的某一个线程唤醒
  • notifyAll():将阻塞等待池中的所有个线程唤醒

sleep()和wait()的异同

相同点:一旦执行方法,都可以使当前线程进入阻塞状态。

不同点:

  1. 两个方法的声明位置不同:sleep()声明在Thread类中,而wait()声明在Object类中
  2. 两个方法的调用要求不同:sleep()可以在任何需要的场景中调用,而wait()必须使用在synchronized代码块或synchronized方法中
  3. 是否释放对象锁:如果两个方法都使用在synchronized代码块或synchronized方法中,sleep()不会释放锁,而wait()会释放锁

练习:实现奇数和偶数交叉打印

public class Odevity {
    public static void main(String[] args) {
        Num num = new Num(1);
        Odd odd = new Odd(num);
        Even even = new Even(num);

        odd.start();
        even.start();
    }
}

class Num{
    private int value;
    public Num(int value) {
        this.value = value;
    }
    public void setValue(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}

class Odd extends Thread{
    private Num num;
    public Odd(Num num){
        this.num = num;
    }
    @Override
    public void run() {
        while(true){
            synchronized(num){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(num.getValue() % 2 == 1){
                    System.out.println("奇数:"+num.getValue());
                    num.setValue(num.getValue()+1);
                }
                num.notifyAll();
                try {
                    num.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

class Even extends Thread{
    private Num num;
    public Even(Num num){
        this.num = num;
    }
    @Override
    public void run() {
        while(true){
            synchronized(num){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(num.getValue() % 2 == 0){
                    System.out.println("偶数:"+num.getValue());
                    num.setValue(num.getValue()+1);
                }
                num.notifyAll();
                try {
                    num.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

练习:实现宠物:打工(消耗体力,消耗清洁度) 洗澡(增加清洁度) ?休息(增加体力)

public class QQpet {
    public static void main(String[] args) {
        Pet pet = new Pet(5,5);
        Work work = new Work(pet);
        Bath bath = new Bath(pet);
        Rest rest = new Rest(pet);

        work.start();
        bath.start();
        rest.start();
    }
}


class Pet{
    private int ph;
    private int cl;

    public Pet(int ph, int cl) {
        this.ph = ph;
        this.cl = cl;
    }

    public int getPh() {
        return ph;
    }

    public void setPh(int ph) {
        this.ph = ph;
    }

    public int getCl() {
        return cl;
    }

    public void setCl(int cl) {
        this.cl = cl;
    }
}

class Work extends Thread{
    private Pet pet;
    public Work(Pet pet){
        this.pet = pet;
    }
    @Override
    public void run() {
        while(true){
            synchronized(pet){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(pet.getPh() > 0 && pet.getCl() > 0){
                    System.out.println("体力:"+pet.getPh()+"  清洁度:"+pet.getCl()+"  工作中!");
                    pet.setPh(pet.getPh()-1);
                    pet.setCl(pet.getCl()-1);
                }
                pet.notifyAll();
                try {
                    pet.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

class Bath extends Thread{
    private Pet pet;
    public Bath(Pet pet){
        this.pet = pet;
    }
    @Override
    public void run() {
        while(true){
            synchronized(pet){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(pet.getCl() == 0){
                    System.out.println("清洁度:"+pet.getCl()+" 洗澡中!");
                    pet.setCl(pet.getCl()+1);
                }
                pet.notifyAll();
                try {
                    pet.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

class Rest extends Thread{
    private Pet pet;
    public Rest(Pet pet){
        this.pet = pet;
    }
    @Override
    public void run() {
        while(true){
            synchronized(pet){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(pet.getPh() == 0){
                    System.out.println("体力:"+pet.getPh()+" 休息中!");
                    pet.setPh(pet.getPh()+1);
                }
                pet.notifyAll();
                try {
                    pet.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

练习:五个人买三张票

public class BuyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket(3);
        One one = new One(ticket);
        Two two = new Two(ticket);
        Three three = new Three(ticket);
        Four four = new Four(ticket);
        Five five = new Five(ticket);

        one.start();
        two.start();
        three.start();
        four.start();
        five.start();
    }
}

class Ticket{
    private int ticket;

    public Ticket(int ticket) {
        this.ticket = ticket;
    }

    public int getTicket() {
        return ticket;
    }

    public void setTicket(int ticket) {
        this.ticket = ticket;
    }
}

class One extends Thread{
    private Ticket ticket;
    public One(Ticket ticket){
        this.ticket = ticket;
    }
    @Override
    public void run() {
            synchronized(ticket){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if(ticket.getTicket()  == 0){
                    System.out.println("车票售空!One没买到车票!");
                    Thread.interrupted();
                }
                if(ticket.getTicket()>0){
                    System.out.println("One已买到车票!"+"  票号:"+ticket.getTicket());
                    ticket.setTicket(ticket.getTicket()-1);
                    Thread.interrupted();
                }
            }
    }
}

class Two extends Thread{
    private Ticket ticket;
    public Two(Ticket ticket){
        this.ticket = ticket;
    }
    @Override
    public void run() {
        synchronized(ticket){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            if(ticket.getTicket()  == 0){
                System.out.println("车票售空!Two没买到车票!");
                Thread.interrupted();
            }
            if(ticket.getTicket()>0){
                System.out.println("Two已买到车票!"+"  票号:"+ticket.getTicket());
                ticket.setTicket(ticket.getTicket()-1);
                Thread.interrupted();
            }
        }
    }
}

class Three extends Thread{
    private Ticket ticket;
    public Three(Ticket ticket){
        this.ticket = ticket;
    }
    @Override
    public void run() {
        synchronized(ticket){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            if(ticket.getTicket()  == 0){
                System.out.println("车票售空!Three没买到车票!");
                Thread.interrupted();
            }
            if(ticket.getTicket()>0){
                System.out.println("Three已买到车票"+"  票号:"+ticket.getTicket());
                ticket.setTicket(ticket.getTicket()-1);
                Thread.interrupted();
            }
        }
    }
}

class Four extends Thread{
    private Ticket ticket;
    public Four(Ticket ticket){
        this.ticket = ticket;
    }
    @Override
    public void run() {
        synchronized(ticket){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            if(ticket.getTicket()  == 0){
                System.out.println("车票售空!Four没买到车票!");
                Thread.interrupted();
            }
            if(ticket.getTicket()>0){
                System.out.println("Four已买到车票!"+"  票号:"+ticket.getTicket());
                ticket.setTicket(ticket.getTicket()-1);
                Thread.interrupted();
            }
        }
    }
}

class Five extends Thread{
    private Ticket ticket;
    public Five(Ticket ticket){
        this.ticket = ticket;
    }
    @Override
    public void run() {
        synchronized(ticket){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            if(ticket.getTicket()  == 0){
                System.out.println("车票售空!Five没买到车票!");
                Thread.interrupted();
            }
            if(ticket.getTicket()>0){
                System.out.println("Five已买到车票!"+"  票号:"+ticket.getTicket());
                ticket.setTicket(ticket.getTicket()-1);
                Thread.interrupted();
            }
        }
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-27 13:57:03  更:2021-09-27 13:58:26 
 
开发: 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 18:58:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码