Java(九)(多线程,线程安全,实现线程的方法,线程同步,线程池,并发和并行,线程的六种状态)

目录

多线程

线程

实现线程的方法

方法一:继承Thread父类

方法二:实现Runnable接口

方法三:Callable接口和FutureTask类来实现

Thread方法

线程安全

线程同步

同步代码块

同步方法

Lock锁

线程池

线程池对象的创建方式一:

线程池处理Runnable任务

线程池处理Callable任务

并发和并行

并发的含义

并行的理解

线程的六种状态


多线程

线程

线程(Thread是一个程序内部单独一条执行流程),程序中如果只有一条执行流程,那这个程序就是单线程的程序

实现线程的方法

方法一:继承Thread父类

我们先写一个MyThread类,表示我们创建的子线程类

public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i <= 5; i++) {
            System.out.println("子线程MyThread输出"+i);
        }
    }
}

我们实现main方法

public class main_Thread {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
        for (int i = 0; i <= 5; i++) {
            System.out.println("主线程main输出:"+i);
        }
    }
}

输出结果为

说明我们现在有两个线程,每一个线程都先运行

需要我们注意的是:

第一点: 我们在子类中重写了run方法,但是我们在调用创建的线程对象t的方法是start(),如果是run()方法会变成单线程,会先执行完run方法里的才会执行main函数中的

第二点: 我们的子线程要放到主线程的前面

缺点:一个子类只能继承一个父类,不能再继承其他类,所以继承了Thread之后,不能继承其他类,会导致功能减少

方法二:实现Runnable接口

(1)定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

public class MyRunnable implements Runnable{
    @Override
    public void run(){
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程1---->"+i);
        }
    }
}

(2)创建MyRunnable任务对象

Runnable r = new MyRunnable();

(3)把MyRunnable任务对象交给Thread处理并且调用对象的start()方法启动线程

new Thread(r).start();

下面是mian方法的完整代码

public class main_Thread {
    public static void main(String[] args) {
        // 创建MyRunnable任务对象
        Runnable r = new MyRunnable();
        new Thread(r).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("mian线程--->"+i);
        }
    }
}

代码优化

不创建子类,用匿名内部类

public class main_Thread {
    public static void main(String[] args) {
        // 创建MyRunnable任务对象

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程--->"+i);
                }
            }
        }).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("mian线程--->"+i);
        }
    }
}

优点: 任务类只能实现接口,可以继续继承其他类,实现其他接口

缺点: 需要多一个Runable对象

方法三:Callable接口和FutureTask类来实现

使用原因:因为我们上面两个方法都是调用start方法,进而让调用run方法的,而run方法是void类型的,不会给我们返回值,这时候就要用到方法三

最大优点:可以返回线程执行完毕后的结果

创建线程的步骤:

1.创建任务对象:

(1)定义一个类实现Callable接口,重写call方法,封装要做的事情,和返回的数据

import java.util.concurrent.Callable;

public class MyCall implements Callable<String> {
    private int n ;

    public MyCall(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        // 描述线程的任务,返回线程执行返回后的结果
        // 需求:求1-n的和返回
        int sum = 0;
        for (int i=1;i<n;i++)
        {
            sum+=i;
        }

        return ""+sum;
    }
}

(2)把Callable类型的对象封装成FutureTask(线程任务对象)

//创建一个Callable对象
        Callable<String> call = new MyCall(100);

2.把线程任务对象交给Thread对象

// 把Callable的对象封装成一个FutureTask对象
        // 未来任务对象的作用?
        // 1.是一个任务对象,实现Runnable对象
        // 2.可以在线程执行完毕后,
        // 用未来线程对象调用get方法获取线程执行完毕后值
        FutureTask<String> f1 = new FutureTask<>(call);

3.调用Thread对象的start方法启动线程

// 把任务对象交给一个Thread对象
        new Thread(f1).start();

4.线程执行完毕后,通过FutureTask对象的get()方法去获取线程任务执行的结果

// 获取线程执行完毕后返回的结果
        // 注意: 如果执行到这,假如说线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果
        String s = f1.get();
        System.out.println(s);

mian主线程中的完整代码


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class main_Thread {
    public static void main(String[] args) throws Exception {
        //创建一个Callable对象
        Callable<String> call = new MyCall(100);
        // 把Callable的对象封装成一个FutureTask对象
        // 未来任务对象的作用?
        // 1.是一个任务对象,实现Runnable对象
        // 2.可以在线程执行完毕后,
        // 用未来线程对象调用get方法获取线程执行完毕后值
        FutureTask<String> f1 = new FutureTask<>(call);
        // 把任务对象交给一个Thread对象
        new Thread(f1).start();

        // 获取线程执行完毕后返回的结果
        // 注意: 如果执行到这,假如说线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果
        String s = f1.get();
        System.out.println(s);

    }
}

Thread方法

public class main_Thread {
    public static void main(String[] args) throws Exception {
        Thread t1 = new MyThread();
        t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName()); // 输出线程1的名字

        Thread t2 = new MyThread();
        t2.setName("2号线程");
        t2.start();
        System.out.println(t2.getName()); // 输出线程2的名字

        // 主线程对象的名字
        Thread m = Thread.currentThread(); // 拿到当前执行线程名字
        m.setName("nb线程");
        System.out.println(m.getName()); // main

        for (int i = 0; i <= 5; i++) {
            System.out.println("main主线程输出:"+i);
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i <=5 ; i++) {
            if(i== 3)
            {
                // 让当前的线程停止5秒
                Thread.sleep(5000);
            }
        }
        // join方法让调用方法的线程先执行完
        Thread t1 = new Thread();
        t1.start();
        t1.join();

    }
}

线程安全

线程安全问题的原因: 多个线程,同时访问同一个共享资源,且存在修改资源

我们来模拟线程安全,假如说现在取钱,小明和小红在同一个账户下取钱,现在账户有10万元,小明在他的手机上取10万元,小红在她的手机取10万元,想有没有一种可能小明的手机判断有钱但是还没有取钱的同时,小红的手机也判断有钱,也要完成取钱操作,这样的话小明取出来了10万元,小红手机也取出了10万元,而账户中只有10万元,这就是线程安全

我们用代码模拟一下这个情况

我们先创建账户类,用来创建账户对象

package surf_Thread;

public class Account {
    private String Id;
    private int money;

    public Account() {
    }

    public Account(String id, int money) {
        Id = id;
        this.money = money;
    }

    public String getId() {
        return Id;
    }

    public void setId(String id) {
        Id = id;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void drawMoney(int i) {
        // 先搞清楚谁来取钱?
        String name = Thread.currentThread().getName();
        // 判断余额是否足够
        if(this.money>=i)
        {
            System.out.println(name+"来取钱"+money+"成功");
            this.money-=i;
            System.out.println(name+"来取钱后,余额剩余: "+money);
        }
        else{
            System.out.println(name + "来取钱,余额不足");
        }
    }
}

我们在上面创建了一个类的取钱方法,因为每个线程都要进入这个方法,我们可以拿到这个线程的名字

下面我们创建一个线程类,run里面要把握住,这个线程的行为,因为我们要完成取钱,所以必须有一个账户对象,我们要构建一个有参构造器,来接收对那个账户进行操作,还要接受线程的名字

package surf_Thread;

public class DrawMoney extends Thread{
    private Account acc;
    public DrawMoney(Account acc,String name)
    {
        super(name); // super 要放到上面
        this.acc = acc;
    }
    @Override
    public void run()
    {
        //run里面主要实现线程要完成什么事情
        acc.drawMoney(100000);
    }
}

主线程

package surf_Thread;

public class ThreadTest {
    public static void main(String[] args) {
        // 1. 创建一个账户,两个人共同的对象
        Account acc = new Account("ICBC-120",100000);
        // 2.创建取钱线程
        new DrawMoney(acc,"小明").start();
        new DrawMoney(acc,"小红").start();


    }
}

我们对这个案例运行时,发现:

这个就会造成线程安全问题

我们应该怎么解决呢?

看下面

线程同步

同步代码块

建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象

对于静态方法建议使用字节码(类名.class)对象作为锁对象

同步方法

有隐含this

Lock锁

线程池

JDK 5.0起提供了代表线程池的接口: ExecutorService

但是利用接口我们是不能创建线程池对象的,那我们如何得到线程池对象?

线程池对象的创建方式一:

使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

需要我们注意的是:

1.临时线程什么时候创建?

新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程

2.什么时候会开始拒绝新任务?

核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务

下面是创建线程池

import java.util.concurrent.*;

public class Pool {
    public static void main(String[] args) {
        ExecutorService Pool = new ThreadPoolExecutor(3,5,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());
        
    }
}

线程池处理Runnable任务

下面是4中不同形式的任务拒绝策略

public class Pool {
    public static void main(String[] args) {
        ExecutorService Pool = new ThreadPoolExecutor(3,5,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());
        Runnable target = new MyRunnable();
        Pool.execute(target); // 线程池自动创建一个新线程,自动处理这个任务,自动执行
        Pool.execute(target); // 线程池自动创建一个新线程,自动处理这个任务,自动执行
        Pool.execute(target); // 线程池自动创建一个新线程,自动处理这个任务,自动执行
        Pool.execute(target); // 复用上面的线程
        Pool.execute(target); // 复用上面的线程 // 注意临时线程的创建条件:核心线程都占用了,而且队列中都占满了
        // 如果想要创建临时线程,让上面的线程都被占用,而且队列占满
        //假如说现在满足条件,那么下面添加任务就开始创建临时线程
        Pool.execute(target);
        Pool.execute(target);  // 上面创建了两个临时线程
        // 如果再有任务那么就直接拒绝
        
    }
}

因为线程池创建之后开始运行,他不能自动关闭,我们一般情况下也不希望它关闭

那么怎么让他关闭呢?

两种方法:

线程池处理Callable任务

package ThreadPoolTest;

import java.util.concurrent.*;

public class Pool {
    public static void main(String[] args) throws Exception {
        ExecutorService Pool = new ThreadPoolExecutor(3,5,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory()
                ,new ThreadPoolExecutor.AbortPolicy());
        Future<String> f1 = Pool.submit(new MyCall(100));
        Future<String> f2 = Pool.submit(new MyCall(200));
        Future<String> f3 = Pool.submit(new MyCall(300));

        String s1 = f1.get();
    }
}

并发和并行

进程: 正在运行的程序(软件)就是独立的进程

线程是属于进程的,一个进程中可以同时运行很多个线程

进程中的多个线程其实是并发和并行的

并发的含义

进程中的线程是由CPU负责执行的,但CPU能同时处理的线程数量有限,为保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发

并行的理解

在同一个时刻上,同时有多个线程在被CPU调度执行

线程的六种状态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/199903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux概述

Linux概述 1、操作系统 ​ 定义&#xff1a;操作系统(Operating System&#xff0c;简称OS)是管理计算机硬件与软件资源的计算机程序 ​ 作用&#xff1a;是把计算机系统中对硬件设备的操作封装起来&#xff0c;供应用软件调用&#xff0c;也是提供一个让用户与系统交互的操…

数据安全建设的六大关键步骤

随着数字化时代的到来&#xff0c;数据安全已经成为企业和社会组织必须面对的重要问题。数据泄露、网络攻击等安全事件频发&#xff0c;给个人隐私、企业利益和国家安全带来了严重威胁。因此&#xff0c;加强数据安全建设已成为刻不容缓的任务。以下是数据安全建设的六大关键步…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展

近年来&#xff0c;国内外学者在生态系统的敏感性、适应能力和潜在影响等方面开展了大量的生态脆弱性研究&#xff0c;他们普遍将生态脆弱性概念与农牧交错带、喀斯特地区、黄土高原区、流域、城市等相结合&#xff0c;评价不同类型研究区的生态脆弱特征&#xff0c;其研究内容…

Vue2 若依框架头像上传 全部代码

<template><div><div class"user-info-head" click"editCropper()"><img v-bind:src"options.img" title"点击上传头像"class"img-circle img-lg" /></div><el-dialog :title"title&…

没AI背景的高级产品经理,可能快做不下去了

‍ 一、现状 1个多月前&#xff0c;我在给某高P团员“1v1咨询”时提到&#xff0c;虽然目前各家AI 2.0公司里&#xff0c;高级坑位都被占了&#xff0c;但春节前后可能会空出来一些。 底层原因在于&#xff0c;今年有一些AI背景较少的高级产品经理&#xff0c;趁热跳到AI公司&a…

高防CDN可以起到什么作用?

高防CDN相对于普通的CDN加速&#xff0c;除了具备基础的加速功效外&#xff0c;高防CDN在每一节点上均有相应配置的防御功效&#xff0c;不仅具备了隐藏源站不被攻击的优势&#xff0c;也具备了访问加速&#xff0c;多节点防御的功效。随着互联网的不断发展&#xff0c;网络上的…

error “you should set MAGICKCORE_HDRI_ENABLE

最近做一个项目需要配置ImageMagick库&#xff0c;本项目配置环境如下&#xff1a; ImageMagick version 7 Operating system, version and so on ubuntu 20.04 Description error "you should set MAGICKCORE_HDRI_ENABLE 查阅网上的资料&#xff1a; 默认的是DMAGICKC…

“华为不造车 只帮车企造好车“ 那么华为到底造不造车

大家好&#xff0c;我是极智视界&#xff0c;欢迎关注我的公众号&#xff0c;获取我的更多前沿科技分享 邀您加入我的知识星球「极智视界」&#xff0c;星球内有超多好玩的项目实战源码和资源下载&#xff0c;链接&#xff1a;https://t.zsxq.com/0aiNxERDq "华为不造车&a…

蓝桥杯day02——第三大的数

题目 给你一个非空数组&#xff0c;返回此数组中 第三大的数 。如果不存在&#xff0c;则返回数组中最大的数。 示例 1&#xff1a; 输入&#xff1a;[3, 2, 1] 输出&#xff1a;1 解释&#xff1a;第三大的数是 1 。 示例 2&#xff1a; 输入&#xff1a;[1, 2] 输出&…

【Java】7. 类型转换和类型判断

7. 类型转换 7.1 基本类型转换 顺箭头&#xff1a;隐式转换&#xff08;自动&#xff09; 逆箭头&#xff1a;强制转换&#xff08;可能造成精度丢失&#xff09; byte a 10; int b a; int c 1000; byte d (byte) c; System.out.println(d); // -24 7.2 包装类型与基…

【SK-learn学习】1.16 概率校准

一、说明 概率校准&#xff0c;指的是对于分类器而言&#xff0c;对应多种类别&#xff0c;概率最大就将样本归入&#xff0c;这个事实没有考虑置信度的问题。sklearn的calibration就是指的这种情形&#xff0c;参考本文。 二、关于sklearn.calibration的概念 执行分类时&#…

程序设计基础中可能出现的简单编程题2(以郑大为主体)

我们在学习编程过程中往往不仅有C语言实验报告&#xff0c;还有程序设计实验报告。程序设计这一科目主要是为了培养我们写代码时的计算思维&#xff0c;养成从问题到代码实现逐步分析&#xff0c;逐步深入的好习惯。前面有一篇文章介绍了部分程序设计实验报告中的编程题&#x…

337. 打家劫舍III (二叉树)

题目 题解 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def rob(self, root: Optional[TreeNode]) ->…

【开题报告】基于卷积神经网络的图像脑部MRI图像分割

论文题目 基于卷积神经网络的图像脑部MRI图像分割 一、选题意义 1.课题研究的目的和意义 1.1选题目的 脑部疾病是高致残致死率的疾病之一&#xff0c;对人们的生活质量和生命安全都有着十分重大的影响&#xff0c;所以各个国家都开始对脑部疾病的研究重视起来。帕金森、脑胶质…

source: command not found错误的解决方法

偶遇的一个问题&#xff0c;因为在网上没有找到对应的解决办法&#xff0c;可能是属于个案&#xff0c;在此记录备忘&#xff0c;同时供大家参考。 问题现象&#xff1a; 执行命令 source /etc/profile时报错&#xff1a; bash: “source: command not found... 问题定位和…

HCIP-十二、BGP常用属性

十二、BGP常用属性 实验拓扑实验需求及解法1.IP 地址已配置&#xff0c;自行测试直连。2.AS100 中运行 OSPF3.AS200 中运行 ISIS4.运行 BGP5.发布 BGP 路由6.修改起源属性 Origin7.修改 AS-path8.修改本地优先 Local-preference9.修改 MED 实验拓扑 实验需求及解法 本实验模拟…

从0开始学习JavaScript--JavaScript 闭包的应用

JavaScript的高级概念中&#xff0c;闭包&#xff08;closure&#xff09;常常是一个让人感到困惑但又强大的概念。在这篇文章中&#xff0c;将深入探讨闭包的概念以及它在JavaScript中的各种应用场景。 什么是闭包&#xff1f; 在JavaScript中&#xff0c;闭包是指一个函数能…

LINUX入门篇【11】---进程篇【3】---进程优先级,进程切换,进程调度

前言&#xff1a; 有了前面知识点的铺垫&#xff0c;本篇我们将围绕进程的三个方面来展开&#xff0c;即进程优先级&#xff0c;进程切换以及进程调度的问题&#xff0c;这里的进程调度其实本质就是CPU是如何去调度进程的。 进程优先级&#xff1a; 优先级的概念&#xff1a…

matlab配置

matlab配置 windowslinux挂载安装MATLAB windows 按照这里一步步配置就行( 移动硬盘中软件备份中自取) linux linux配置步骤 挂载 sudo mount -t auto -o loop /media/oyk/Elements/ubuntu/MATLAB/R2017a_glnxa64_dvd1.iso ./matlab/安装MATLAB 挂载完成后&#xff0c;先…
最新文章