java多线程详解

发布时间:2024年01月25日

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。简单理解为:引用软件中相互独立,可以同时允许的功能

进程是程序的基本执行实体

并发:在同一时刻,有多个指令在单个CPU上交替执行

并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程的实现方式

  • 继承Thread类的方式进行实现
  • 实现Runnable接口进行实现
  • 利用Callable接口和Future接口的方式实现

方式一:

//将类声明为Thread类的子类,重写Thread类的run方法。
class MyThread() extends Thread{
    public void run(){
        //这里书写要执行的代码
        System.out.print("hello,xun");
        //
        System.out.print(getName())//获取当前线程的线程名
    }
}

MyThread mythread = new MyThread();
mythread.start();//启动线程

mythread.setName("线程一")//给线程起名字

方式二:

//声明实现Runnable接口的类,该类实现run方法。
class MyThread() implements Runnable{
    public void run(){
        //这里书写要执行的代码
        System.out.print("hello,xun");
        //获取当前线程对象
        Thread t = Thread.currentThread();
        t.getName();//获取当前线程的线程名
    }
}
//表示多线程要执行的任务
MyThread mythread = new MyThread();
//创建线程对象
Thread t = new Thread(mythread);
//开启线程
t.start(); 

t.setName("线程一")//给线程起名字

方式三:

//利用Callable接口和Future接口的方式实现
//特点:可以获取到多线程运行的结果
class MyCallable implements Callable<Integer>{//该线程返回值的类型
    public Integer call() throws Exception{
        //求1—100之间的和
        int sum=0;
        for(int i = 1;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}
//创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用是管理多线程的运行结果)
FutureTask<Integer> ft = new FutureTask<>(mc);
//创建线程的对象
Thread t = new Thread(ft);
t.start();
//获取多线程的运行结果
Integer res = ft.get();

在这里插入图片描述

多线程中常用的成员方法

在这里插入图片描述

  1. 如果我们没有给线程设置名字,线程也是有默认名字的,格式:Thread-X(X是序号,从0开始)
  2. 如果给线程设置名字,有两种方法,1.通过setName设置线程名 2.通过构造函数设置线程名
  3. 当JVM虚拟机启动后,会自动运行多条线程,其中有一条线程叫main线程,他的作用就是调用main方法,执行里面的代码
  4. 线程的优先级,最小的是1,最大的10,默认是5;优先级越大,抢到CPU的概率越大
  5. 当其他的非守护线程执行完毕之后,守护线程会陆续结束(可能不会将自己的程序运行完)
  6. t.join()//表示将线程t插入到当前线程之前

线程的声明周期

在这里插入图片描述

线程安全问题

线程的执行有随机性,这样会导致线程存在安全问题

解决方法:

同步代码块:把操作共享的代码锁起来

synchronized(锁对象){
	//操作共享的代码块
}
//特点一:锁默认打开,有一个线程进去了,锁自动关闭
//特点二:里面的代码全部执行完毕,线程出来,锁自动打开
//锁对象一定是唯一的,一般是锁对象的字节码文件对象
synchronized(thread.class);

同步方法:把synchronized关键字加到方法上

格式:修饰符 synchronized 返回值类型 方法名(方法参数){...}
//特点一:同步方法是锁住方法里面的所有方法
//特点二:锁对象不能自己指定
	非静态方法:this
    静态方法:当前类的字节码文件对象

为了更加清晰的表达如何释放锁,获得锁,JDK5之后提供了一个锁对象Lock

void lock()//获得锁
void unlock()//释放锁
    //手动释放锁,手动获得锁

Lock是接口不能被直接实例化,使用它的实现类ReentrantLock来实例化

死锁

出现了锁的嵌套

产生因素

1、系统拥有的资源数量
2、资源分配策略
3、进程对资源的使用要求
4、并发进程的推荐顺序

必要条件

  • 互斥条件:进程互斥使用资源
  • 占有和等待条件:进程申请资源得不到时,不会释放已经占有的资源
  • 不剥夺条件:一个进程不能抢占其他进程的资源
  • 循环等待条件:存在一组进程循环等待资源

死锁的防止

破坏产生死锁的任意一个条件即可

生产者和消费者(等待唤醒机制)

生产者:生产数据

消费者:消费数据

常见方法:

public void wait();//当前线程等待,知道被其他线程唤醒

public void notify();//随机唤醒一个线程

public void notifyAll();//唤醒所有线程

线程的状态

在这里插入图片描述

新建状态(new)			->			创建线程对象

就绪状态(Runnable)			->			start方法

阻塞状态(blocked)			->			无法获得锁对象

等待状态(waiting)			->			wait方法		

计时状态(timed_waiting)			->			sleep方法

结束状态(terminated)			->			全部代码运行完毕

线程池

以前使用多线程的弊端:

? 用到线程时就创建

? 用完之后线程消失

这样会浪费操作系统的资源

线程池:类似于一个容器,里面有线程,需要用到线程时,直接取即可,用完还回去。

线程池有最大线程数量限制,可以自己设置

1. 创建一个空池子
2. 提交任务时,池子会创建新的线程对象,任务执行完毕,归还线程对象到线程池,下次调用时,直接使用,不需要创建
3. 如果提交任务时,没有空闲线程,也无法创建新的线程,任务就会排队等待

线程池:类似于一个容器,里面有线程,需要用到线程时,直接取即可,用完还回去。

线程池有最大线程数量限制,可以自己设置

1. 创建一个空池子
2. 提交任务时,池子会创建新的线程对象,任务执行完毕,归还线程对象到线程池,下次调用时,直接使用,不需要创建
3. 如果提交任务时,没有空闲线程,也无法创建新的线程,任务就会排队等待
文章来源:https://blog.csdn.net/qq_60749185/article/details/135833488
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。