java多线程超详细到极致的内容!

Java多线程超详细笔记 System.out.println(Thread.currentThread().getName()+“正在出售第”+(tickets–)+“张票”);System.out.println(Thread.currentThread().getName()+“正在出售第”+(tickets–)+“张票”);System.out.println(Thread.currentThread().getName()+“正在出售第”+(tickets–)+“张票”);所谓代码块就是使用{}括起来的一段代码; 阅读详情

1. 线程概述

1.1 线程和进程

  • 进程是处于运行过程中的程序,并且具有一定的独立功能
  • 并发性:同一个时刻只能有一条指令执行,但多个进程指令被快速轮换执行
  • 并行:多条指令在多个处理器上同时执行
  • 线程是进程的执行单元

1.2 多线程的优势

  • 进程之间不能共享内存,但线程之间非常容易
  • 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程效率更高
  • Java语言内置了多线程功能

2. 线程创建与启动

2.1 继承Thread

public class FirstThread extends Thread {
    private int i;

    @Override
    public void run() {
        for(i = 0; i < 50; i ++){
            System.out.println(this.getName() + "" + i);
        }
    }

    public static void main(String[] args){
        FirstThread ft = new FirstThread();
        for(int i =0; i < 100;i ++){
            System.out.println(Thread.currentThread().getName() + "" + i);
            if(i == 20) {
                ft.run();
            }
        }
    }
}

2.2 实现Runnable接口

public class FirstThread implements java.lang.Runnable {
    private int i;

    public void run() {
        for(i = 0; i < 50; i ++){
            System.out.println(Thread.currentThread().getName()+ "" + i);
        }
    }

    public static void main(String[] args){
        FirstThread ft = new FirstThread();
        for(int i =0; i < 100;i ++){
            System.out.println(Thread.currentThread().getName() + "" + i);
            if(i == 20) {
                ft.run();
            }
        }
    }
}

2.3 使用Callable和Future

  • Callable接口提供了一个call()方法可以作为线程执行体,call()方法有返回值且可以声明抛出异常
  • Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类
  • Future接口定义的方法:
方法名作用
boolean cancel(boolean mayInterruptIfRunning)试图取消该Future里关联的Callable任务
V get()返回Callable任务里call方法的返回值,该方法会造成线程阻塞,等子线程执行完才能获得
V get(long timeout, TimeUnit unit)返回Callable任务里call方法的返回值。该方法让程序最多阻塞timeout和unit指定的时间,如果经过指定时间Callable任务还没有返回值则抛出TimeoutException异常
boolean isCancelled()Callable中的任务是否取消
boolean isDone()Callable中的任务是否完成
public class CallableDemo {
    public static void main(String[] args){
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)() -> {
            int i = 0;
            for( ; i < 100; i++){
                System.out.println(i);
            }
            return i;
        });
        new Thread(task).start();
        try {
            System.out.println(task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

2.4 创建线程的三种方式对比

Runnable和Callable优劣势:

  • 线程类只是实现了Runnable、Callable接口,还可以继承其他类
  • Runnable和Callable情况下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况
  • 编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()

Thread优劣势:

  • 线程类已经继承了Thread类,所以不能再继承其他父类
  • 编写简单,如果需要访问当前线程,用this使用

3. 线程生命周期

3.1 新建和就绪状态

  • new语句仅仅由Java虚拟机为其分配内存,并没有表现出任何线程的动态特征
  • 如果直接调用继承类的run方法,则只会有MainActivity,而且不能通过getName获得当前执行线程的名字,而需用Thread.currentThread().getName()
  • 调用了run方法后,该线程已经不再处于新建状态

3.2 运行和阻塞状态

  • 当线程数大于处理器数时,存在多个线程在同一个CPU上轮换的现象
  • 协作式调度策略:只有当一个线程调用了sleep()或yield()方法才会放弃所占用的资源——即必须线程主动放弃所占用的资源
  • 抢占式调度策略:系统给每个可执行的线程分配一个小的时间段来处理任务,当任务完成后,系统会剥夺该线程所占用的资源
  • 被阻塞的线程会在合适的时候重新进入就绪状态

image

3.3 死亡状态

  • 测试线程死亡可用isAlive()
  • 处于死亡的线程无法再次运行,否则引发IllegalThreadStateException异常

4. 控制线程

4.1 join线程

  • 在MainActivity调用了A.join(),则MainActivity被阻塞,A线程执行完后MainActivity才执行

4.2 后台线程(Daemon Thread)

  • 如果所有的前台线程都死亡,后台线程会自动死亡
public class DaemonThread extends Thread {
    @Override
    public void run() {
        for(int i = 0; i< 1000; i++){
            System.out.println("DaemonActivity" + i);
        }
    }

    public static void main(String[] args){
        DaemonThread thread = new DaemonThread();
        thread.setDaemon(true);
        thread.start();
        for(int i = 0; i < 10; i ++ ){
            System.out.println("MainActivity" + i);
        }
    }
}

image

4.3 线程睡眠sleep

 try {
      Thread.sleep(200);
} catch (InterruptedException e) {
       e.printStackTrace();
}

  • sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程优先级;但yield方法只会给优先级相同或更高的线程
  • sleep方法将转入阻塞状态,直到经过阻塞时间才会转入就绪;yield强制当前线程转入就绪状态
  • sleep方法抛出了InterruptedException,yield方法没抛出异常

4.4 改变线程优先级

  • 优先级高的线程获得较多的执行机会,优先级低的线程获得较少的执行机会
  • setPriority和getPriority方法来设置和返回指定线程的优先级

5. 线程同步

  • run()方法不具有同步安全性
  • ***java***引入了同步监视器来解决多线程同步问题,sychronized(obj)中obj就是共享资源

5.1 同步方法

  • 同步方法就是使用synchronized来修饰某个方法
  • 实例方法的同步监视器默认是this
  • ***Java***中不可变类总是线程安全的,可变类对象需要额外的方法来保证其线程安全
public class DaemonThread extends Thread {

    static int balance = 100;
    int drawAmount;
    String name;

    public DaemonThread(int drawAmount, String name){
        this.drawAmount = drawAmount;
        this.name = name;
    }

    @Override
    public void run() {
        this.draw(drawAmount);
    }

    public synchronized void draw(int amount){
        if(balance  >= amount){
            System.out.println(this.name + "取出了" + amount);
            try{
                Thread.sleep(1);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            balance -= amount;
            System.out.println("\t余额为" + balance);

        } else{
            System.out.println(this.name + "取现失败");
        }
    }
    public static void main(String[] args){
        new DaemonThread(50, "A").start();
        new DaemonThread(100, "B").start();
    }
}

5.2 释放同步监视器的锁定

下列情况下,线程会释放对同步监视器的锁定

  • 当前线程的同步方法、同步代码块执行结束
  • 遇到了break、return
  • 遇到异常
  • 程序执行了同步监视器对象的wait()方法

下列情况下不会释放:

  • 执行同步方法时,程序调用Thread.sleep() Thread.yield()方法
  • 其他线程调用了该线程的suspend方法

5.3 同步锁

  • ***Java5***开始,提供了一种功能更强大的同步锁机制,可以通过显式定义同步锁对象来实现同步
  • Lock提供了比synchronized更广泛的锁定操作,并且支持多个相关的Condition对象
  • Lock类型:
    Lock
    ReadWriteLock
    ReentrantLock:常用,可以对一个加锁的对象重新加锁
    ReentrantReadWriteLock
    StampedLock
方法名作用
lock加锁
unlock解锁

5.4 死锁

A等B,B等A

5.5 线程通信

5.5.1 传统的线程通信

方法名作用
wait导致当前线程等待,直到其他线程调用该同步监视器的notify()或notifyAll()方法
notify唤醒在此同步监视器等待的单个线程
notifyAll唤醒在此同步监视器等待的所有线程
  • wait()必须在加锁的情况下执行

5.5.2 使用Condition

  • 如果系统中不适用synchronized来保证线程同步,而使用Lock对象来保证同步,那么无法使用wait,notify,notifyAll()来进行线程通信
  • 当使用Lock对象,***Java***提供Condition保证线程协调
  • Condition方法如下
方法名作用
await导致当前线程等待,直到其他线程调用该同步监视器的signal()或signalAll()方法
signal唤醒在此Lock对象的单个线程
signalAll唤醒在此Lock对象的所有线程

5.5.3 使用阻塞队列

  • ***Java***提供了一个BlockingQueue接口
  • 当生产者线程试图向BlockingQueue放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue取出元素时,如果该队列已空,则该线程被阻塞
方法名作用
put(E e)尝试把E元素放入BlockingQueue
take()尝试从BlockingQueue的头部取出元素
public class BlockingQueueThread extends Thread {

    private BlockingQueue<String> bq;

    public BlockingQueueThread(BlockingQueue<String> bq){
        this.bq = bq;
    }

    @Override
    public void run() {
        String[] strColl = new String[]{
                "Java",
                "Kotlin",
                "JavaScript"
        };

        for(int i = 0; i < 1000; i ++){
            try {
                System.out.println(getName() + "开始动工" + i);
                bq.put(strColl[i % 3]);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(getName() + "工作结束");
    }

    public static void main(String[] args){
        BlockingQueue<String> bq = new ArrayBlockingQueue<>(5);
        new BlockingQueueThread(bq).start();
    }
}

image

可以看到,当Thread-0运行到第6次时就已经被阻塞,不能往里添加内容

6. 线程组和未处理的异常

  • ThreadGroup表示线程组,可以对一批线程进行分类管理
  • 子线程和创建它的父线程在同一个线程组内
  • ThreadGroup方法
方法名作用
int activeCount返回线程组中活动线程的数目
interrupt中断此线程组中所有活动线程的数目
isDaemon线程组是否是后台线程组
setDaemon设置后台线程
setMaxPriority设置线程组的最高优先级

7. 线程池

  • 线程池在系统启动时即创建大量空闲的线程
  • 程序将一个Runnable对象或Callable对象传给线程池,线程池就会启动一个空闲线程来执行他们
  • 线程结束不死亡,而是回到空闲状态
  • ***Java8***之后新增了一个Executors工厂类来生产线程池

7.1 ThreadPool

public class ThreadPoolTest {

    public static void main(String[] args){
        ExecutorService pool = Executors.newFixedThreadPool(2);

        java.lang.Runnable target = () -> {
           for (int i = 0; i < 100 ; i ++){
               System.out.println(Thread.currentThread().getName() + "的i为" +i);
           }
        };

        pool.submit(target);
        pool.submit(target);
        pool.shutdown();
    }
}

image

7.2 ForkJoinPool

  • 将一个任务拆分成多个小任务并行计算,再把多个小任务的结果合并成总的计算结果
  • ForkJoinPool是ExecutorService的实现类
public class PrintTask extends RecursiveAction {

    public static int THREADSH_HOLD = 50;

    private int start;

    private int end;

    public PrintTask(int start, int end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        if(end - start < THREADSH_HOLD){
            for(int i = start; i < end; i ++){
                System.out.println(Thread.currentThread().getName() + "的i为" + i);
            }
        } else {
            PrintTask left = new PrintTask(start, (start + end) / 2);
            PrintTask right = new PrintTask((start + end) / 2 , end);
            left.fork();
            right.fork();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        PrintTask printTask = new PrintTask(0 , 300);
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(printTask);
        pool.awaitTermination(2, TimeUnit.SECONDS);
        pool.shutdown();

    }
}

image

神经网络基础概念总结(小白入门自用) 神经网络人工神经网络多层感知机激活函数反向传播算法损失函数权值初始化正则化方法 人工神经网络 人工神经元:人工神经元是人工神经网络的基本单元。模拟生物神经元,人工神经元有1个或者多个输入(模拟多个树突或者多个神经元向该神经元传递神经冲动);对输入进行加权求和(模拟细胞体将神经信号进行积累和树突强度不同);对输入之和使用激活函数计算活性值(模拟细胞体产生兴奋或者抑制);输出活性值并传递到下一个人工神经元(模拟生物神经元通过轴突将神经冲动输入到下一个神经元)。 人工神经网络:大量的人工神经元以某种链接方式得来的 阅读详情

相关推荐

达梦数据库与sharding-jdbc的深度适配实践

本文详细介绍了达梦数据库与sharding-jdbc的深度适配实践,包括驱动与依赖管理、核心适配实现、注册与配置等关键步骤。通过扩展shardingSphere的方式,成功实现了达梦数据库的分库分表功能,并提供了常见问题解决方案和性能优化建议,助力企业级应用高效处理海量数据。

weixin_30300225的博客 625

Java多线程(超详解)

Java多线程(超详解)

m0_52896041的博客 1万+

PSO算法介绍

结合网上的文章对PSO进行一些介绍,并附上自己复现的算法。

Witty_Hou的博客 1万+

java多线程详解

多线程的实现方式

xyx328的博客 1391

IAR使用教程:如何导入现有工程并成功编译

详细介绍如何在IAR中导入现有工程项目并完成编译配置,解决常见报错问题。通过清晰的操作指引,帮助开发者快速上手iar使用教程中的关键环节,提升开发效率。

weixin_42437253的博客 884

【agent实战教程】OpenClaw多个agent一文通

一个合格的独立agent,必须具备6个完全独立的专属组件独立组件路径规则核心要求不独立的严重后果唯一agentId配置文件中全局唯一,不能重复,建议用英文+数字,不要有特殊字符agent配置冲突,消息派单错误,会话串线独立工作区(workspace)建议每个agent一个独立文件夹,不要共用人设、记忆、规则混在一起,agent人设崩塌,对话串线独立状态目录(agentDir)固定系统自动生成,绝对禁止多个agent复用同一个agentDir。

海兰 651

java多线程(超详细)

进程就是正在运行中的程序(进程是驻留在内存中的)是系统执行资源分配和调度的独立单位每一进程都有属于自己的存储空间和系统资源注意:进程A和进程B的内存独立不共享。

zdl66的博客 10万+

安全运营面试总结

答案可以在我的博客中查找 1.自我介绍 2.TCP/IP七层模式 3.端口号(常见协议 数据库) 4.Owasp top 10 5.渗透测试过程 6.漏扫工具 7.中间件漏洞 8.应急响应 9.项目 10.SQL注入类型 11.TCP 三次握手 四次挥手 12.你有什么要问我的 1.自我介绍 2.项目介绍 3.宽字节注入的原理 4.渗透流程 5....

qq_51577576的博客 4413

代码理解java多线程 (一) - 基础篇(1)

第一章 进程与线程的基本概念 1.1 进程产生的背景 最初的计算机只能接受一些特定的指令,用户每输入一个指令,计算机就做出一个操作。当用户在思考或者输入时,计算机就在等待。这样效率非常低下,在很多时候,计算机都处在等待状态。 批处理操作系统 后来有了批处理操作系统,把一系列需要操作的指令写下来,形成一个清单,一次性交给计算机。用户将多个需要执行的程序写在磁带上,然后交由计算机去读取并逐个执行这些程序,并将输出结果写在另一个磁带上。 批处理操作系统在一定程度上提高了计算机的效率,但是由于批处理操作系

zzqtty的博客 416

排队论 | 排队论建模方法

排队论 | 排队论建模方法

算法如诗的博客 356

信贷模型域——客户管理阶段模型(贷中模型)

本文主要探讨了信贷模型域中的客户管理阶段模型,重点聚焦于交叉销售模型。交叉销售模型不仅应用于贷中阶段,还贯穿于贷前、贷中、贷后全过程,其核心作用在于提升客户价值、增强客户粘性、降低流失率以及实现风险与收益的平衡。文中通过具体案例展示了交叉销售模型在现实业务中的应用,并指出其在贷中阶段的主要任务是提升存量客户价值、增加产品使用率,同时有效控制风险。

庄小焱 1886

必知必会的Java多线程可算是被讲透彻了,让我们一起深入浅出多线程!

Java 提供了多线程编程的内置支持,让我们可以轻松开发多线程应用。Java 中我们最为熟悉的线程就是 main 线程——主线程。一个进程可以并发多个线程,每条线程并行执行不同的任务。线程是进程的基本单位,是一个单一顺序的控制流,一个进程一直运行,直到所有的“非守护线程”都结束运行后才能结束。这里简要述说以下并发和并行的区别。并发:同一时间段内有多个任务在运行并行:同一时间点上有多个任务同时在运行多线程可以帮助我们高效地执行任务,合理利用 CPU 资源,充分地发挥多核 CPU 的性能。

老男孩的架构路 581

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Java多线程(超详细!)

1、什么是进程?什么是线程? 进程是:一个应用程序(1个进程是一个软件)。 线程是:一个进程中的执行场景/执行单元。 注意:一个进程可以启动多个线程。 eg.对于java程序来说,当在DOS命令窗口中输入: java HelloWorld 回车之后。 会先启动JVM,而JVM就是一个进程。 JVM再启动一个主线程调用main方法。 同时再启动一个垃圾回收线程负责看护,回收垃圾。 最起码,现在的java程序中至少有两个线程并发, 一个是垃圾回收线程,一个是执行main方法的主线程。 3、进程和线程是什么关系?

一只小菜鸡 15万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java多线程学习(吐血超详细总结)

本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。

Evankaka的专栏 43万+

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Java多线程知识点,看这一篇就够了!(超详细)

1、饿汉模式(迫切)程序启动,类加载之后,立即创建出实例2、懒汉模式(延时)在第一次使用实例的时候,再创建,否则能不创建就不创建举个例子:编译器打开一个文件,假设有一个超大的文件,

weixin_73616913的博客 5491

JAVA多线程详解(超详细)

线程简介,线程的创建方式,多线程并发问题

七星太极阵的博客 2万+

java多线程(超详细!)

Java 的多线程是一种允许在一个程序中同时运行多个线程的技术。每个线程是独立的执行路径,可以并发执行,从而提高程序的效率和响应能力。

小程xy的博客 2178

Java 并发(多线程)超详细

超级详细解析了 Java 多线程的应用等

qq_42689380的博客 1万+

Java 实现多线程的四种方式 超详细

java线程池

PassionAnytime的博客 1万+

超详细JAVA基础——多线程

学会Java多线程,只看一个博客就够了!

qq_45781655的博客 2万+

Java 多线程(超详细)

多线程学习思路:为什么学习线程?为了解决CPU利用率问题,提高CPU利用率。 =》 什么是进程?什么是线程? =》 怎么创建线程?有哪几种方式?有什么特点? =》 分别怎么启动线程? =》 多线程带来了数据安全问题,该怎么解决? =》 怎么使用synchronized(同步)决解? =》使用同步可能会产生死锁,该怎么决解? =》 线程之间是如何通信的? =》 线程有返回值吗?该如何拿到? =》 怎么才能一次性启动几百上千个的线程? 线程的概念 什么是进程 进程是操作系统中正在执行的不同的应用程序,例如:我

系统看你学习很认真,随机送了一个月会员! 2万+

Java中的多线程编程(超详细总结)

** Java中的多线程编程(超详细总结) ** 一、线程与多线程的概念 线程,即单线程,是程序的一条执行线索,执行路径,是程序使用cpu的最小单位。线程本身不能运行,它只能运行在程序中,线程是依赖于程序存在的。 多线程,从字面上理解,就是从多个单线程一起执行多个任务。在Java 编程中,已经给多线程编程提供了内置的支持。多线程是多任务的一种特别的形式,但多线程使用了更小的cpu资源开销。 多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。 二、线程与进程之间的关系 进程: 一个进程包括由操

qq_39350172的博客 2952

java多线程使用详解与案例,超详细

java多线程使用 java使用多线程的三种方式: 继承Thread类,并重写run方法。 实现Runnable接口,重写run方法 实现Callable<返回值>接口,重写call方法 实例如下 继承Thread class test extends Thread{ @Override public void run() { System.out.println("你好啊"); } } 开始启动线程: new test().start() 实现R

languageStudent的博客 3万+

【十三】Java多线程(指尖上的多线程[超详细])

多线程特别重要,虽然内容偏多,但是需要熟练掌握。面试也会在此章节有考验的!请大家耐心学习! 目录 一、什么是线程 二、线程的组成(创建线程方式) 三、线程的状态(方法实例详解) 四、线程安全(实例详解) 五、多线程之消费者与生产者、死锁 六、线程通信(实例详解) 七、线程池 八、线程安全的集合 思维导图参考: 【十三】Java多线程思维导图 【知识扩展】 你知道synchronize...

奈何的人生 4365
上一篇: java开发?你就得去了解JVM!
下一篇: 你会Spring Boot吗?精讲Spring Boot!
Java_Yhua
博客等级 码龄6年 51粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值