Java多线程

概念

线程的相关概念

  • 程序:Program,是一个CPU运行指令的集合
  • 进程:Process,(正在执行中的程序)是一个静态的概念
    • 进程是程序的一次静态态执行过程,占用特定的地址空间
    • 每个进程都是独立的,由3部分组成cpu,data,code
    • 缺点:内存的浪费,Cpu的负担
  • 线程:是进程中一个"单一的连续控制流程(a singles Threadequentialflow of control)/执行路径
    • 线程又被称为轻量级进程
    • 一个进程可拥有多个并行的进程
    • 一个进程中的线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象。而且他们从同一堆中分配对象->通信、数据操作、同步操作
    • 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
  • 一个进程中至少有一个线程(主线程)

java中的进程和线程

  • Java虚拟机启动的时候会有一个进程Java.exe,该进程中至少有一个线程,在负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程
  • 一个进程中的线程共享代码和数据空间
  • 线程结束,进程未毕结束,但进程结束,线程一定结束。
  • 进程中包含线程,线程是进程的一部分

进程与线程的区别

区别进程线程
根本区别作为资源分配的单位调度和执行的单位
开销每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
所处环境在操作系统中能同时运行多个任务(程序)在同一应用程序中有多个顺序流同时执行
分配内存系统在运行的时候会为每个进程分配不同的内存区域除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源
包含关系没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个线程则执行过程不是一条线的,而是多条线(线程) 共同完成的线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。

Java实现多进程

  • 在Java中负责线程的这个功能的是Java.lang.Thread 这个类
  • 可以通过创建Thread的实例来创建新的线程
  • 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体
  • 通过调用Thead类的start0方法来启动一个线程

线程的创建方式一(集成Thread类)

  1. 集成Thread类
  2. 重写run方法
  3. 创建对象、调用start方法,启动线程
public class ThreadDemo  extends Thread{

    @Override
    public void run() {
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--------------"+i);
        }
    }

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
        for(int i =0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"==========="+i);
        }

    }
}

线程的创建方式二(实现Runnable接口)

  1. 实现Runnable接口
  2. 重写run方法
  3. 创建Thread对象(构造函数创建)
  4. 调用start方法、启动线程
public class RunnableDemo implements Runnable {
    @Override
    public void run() {
        for(int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--------------"+i);
        }
    }

    public static void main(String[] args) {
        RunnableDemo runnableDemo = new RunnableDemo();
        Thread thread = new Thread(runnableDemo);
        thread.start();
        for(int i =0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+"==========="+i);
        }
    }
}

案例实现(卖票)

继承Thread实现

package com.wanglin.study.ticket;
/**
 * 
 * @author wanglin
 * @date 2023/09/08 14:53
 **/
public class TicketThread extends Thread{

    //5张票
    private static Integer ticket = 5;

    @Override
    public void run() {
        for (int i = 10;i<100;i++){
           if (ticket > 0){
               System.out.println(Thread.currentThread().getName() + "正在出售第---------------" + (ticket--) + "------张票");
           }
        }
    }

    public static void main(String[] args) {
        TicketThread thread = new TicketThread();
        TicketThread thread1 = new TicketThread();
        TicketThread thread2 = new TicketThread();
        TicketThread thread3 = new TicketThread();
        TicketThread thread4 = new TicketThread();
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

Runnable接口实现:

package com.wanglin.study.ticket;
/**
 * 
 * @author wanglin
 * @date 2023/09/08 15:03
 **/
public class TicketRunnable implements Runnable{

    private Integer ticket = 5;

    @Override
    public void run() {
        for (int i = 1;i<100;i++){
            if (ticket > 0){
                System.out.println(Thread.currentThread().getName() + "正在出售第---------------" + (ticket--) + "------张票");
            }
        }
    }

    public static void main(String[] args) {
        TicketRunnable ticketRunnable = new TicketRunnable();
        Thread t1 = new Thread(ticketRunnable);
        Thread t2 = new Thread(ticketRunnable);
        Thread t3 = new Thread(ticketRunnable);
        Thread t4 = new Thread(ticketRunnable);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

对比

  • 继承Thread类方式的缺点:那就是如果我们的类已经从一个类继(如小程序必须继承自 Applet 类),则无法继承 Thread(Java是单继承)
  • 通过Runnable接口实现多线程
  • 优点:可以同时实现继承别的类。实现Runnable接口方式要通用一些
    • 避免单继承
    • 方便共享资源同一份资源多个代理访问

线程的状态

  • 新生状态
    • 用new关键词新建一个线程后、该线程对象就除于新生状态
    • 处于新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态
  • 就绪状态
    • 处于就绪状态的线程具备了运行的条件、但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU
    • 当系统选定一个等待执行的线程(就绪状态)后,它就会从就绪状态进入执行状态,该动作称为“CPU调度”
  • 运行状态
    • 在运行状态的线程执行自己的run方法中的代码,直到等待某资源而阻塞或完成任务而死亡。
    • 如果线程在给定的时间片内没有执行结束,就会被系统换下来回到等待执行状态
  • 阻塞状态
    • 处于运行状态的线程在某些情况下,比如执行了sleep方法,或等待I/O设备资源,将让出CPU并暂时停止自己运行,进入阻塞状态
    • 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程变转入就绪状态,重新导就绪队列中排队等待,被系统选中后从原来停止的位置开始继续执行
    • Join方法(代码执行逻辑)
  • 死亡状态
    • 死亡状态是线程生命周期的最后一个阶段。线程死亡的原因有三个
      • 正常运行的线程完成了他的全部工作
      • 线程被强制性终止、比如通过调用stop()方法终止线程(不推荐)
      • 线程抛出未捕获异常

线程相关的操作方法

序号方法名称描述
1public static Thread currentThread()返回目前正在执行的线程
2public final String getName()返回线程的名称
3public final int getPriority()返回线程的优先级
4  public final void setPriority(int newPriority)设置线程的优先级
5public final boolean isAlive()判断线程是否在活动,如果是,返回true,否则返回false
6public final void join()调用该方法的线程强制执行,其它线程处于阻塞状态,该线程执行完毕后,其它线程再执行
7public static void sleep(long millis)使用当前正在执行的线程休眠millis秒,线程处于阻塞状态
8public static void yield()当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待执行的线程,这个时候当前线程就会马上恢复执行。
9public final void stop()强迫线程停止执行。已过时。不推荐使用

线程同步与死锁

多线程的运行出现了安全性问题

线程同步(加锁)

  • 使用同步代码块解决线程安全问题
  • 使用同步方法解决线程安全问题

        同步前提:

  • 必须有两个或两个以上的线程
  • 必须是多线程使用同一资源
  • 必须保证同步中只有一个线程在执行
package com.wanglin.study.ticket;
/**
 * 
 * @author wanglin
 * @date 2023/09/08 15:03
 **/
public class TicketRunnable implements Runnable{

    private Integer ticket = 5;

    @Override
    public void run() {
        for (int i = 1;i<100;i++){
            synchronized (this){
                if (ticket > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第---------------" + (ticket--) + "------张票");
                }
            }

        }
    }

    public static void main(String[] args) {
        TicketRunnable ticketRunnable = new TicketRunnable();
        Thread t1 = new Thread(ticketRunnable);
        Thread t2 = new Thread(ticketRunnable);
        Thread t3 = new Thread(ticketRunnable);
        Thread t4 = new Thread(ticketRunnable);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

使用关键词synchronized实现代码同步(加锁),通常将当前对象作为同步对象。其理论是在需要操作资源的代码片段上加锁,实现同一时刻只有一个线程能够操作资源。

缺点:效率低,在高并发中使用CAS机制

  • 同步监视器
    • synchronized(obj)0中的obj称为同步监视器
    • 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监
      视器
    • 同步方法中无需指定同步监视器,因为同步方法的监视器是this,也就是该对象
      本身
  • 同步监视器的执行过程
    • 第一个线程访问,锁定同步监视器,执行其中代码
    • 第二个线程访问,发现同步监视器被锁定,无法访问
    • 第一个线程访问完毕,解锁同步监视器
    • 第二个线程访问,发现同步监视器未锁,锁定并访问

死锁

  • 同步可以解决线程安全问题、但是过多使用同步会产生死锁问题
  • 死锁一般情况表示线程互相等待,是程序运行出现的问题

线程的生产者和消费者

  • 生产者不断生产,消费者不断取走生产者生产的产品
  • 生产者生产产品放到一个区域,之后消费者从此区域取出产品

线程通信

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值