Java进阶(再论线程)——线程的4种创建方式 线程的生命周期 线程的3大特性 集合中的线程安全问题

news2024/12/24 2:53:27

在这里插入图片描述

前言

多线程作为编程语言中的难点,虽然初级程序员可能很少遇到线程相关的开发任务,但是作为程序员,持续学习和保持对编程的热爱,要求我们对于线程也需要有一定的认识。

本篇博客介绍Java中创建线程的4种方式,并进行了简单的对比;介绍了线程的生命周期,几个关键方法的作用;然后阐述了线程的三大特性,最后结合Java集合框架分析了线程安全的问题。

其他关于Java线程的文章如下:

  • Java进阶(5)——创建多线程的方法extends Thread和implements Runnable的对比 & 线程池及常用的线程池
  • Java进阶(6)——抢购问题中的数据不安全(非原子性问题)& Java中的synchronize和ReentrantLock锁使用 & 死锁及其产生的条件

文章目录

  • 前言
  • 引出
  • 一、创建多线程的方式
    • 1、继承Thread类
      • 当前线程:Thread.currentThread()
    • 2、实现Runable接口
    • 3、实现Callable接口
    • 4、线程池
  • 二、线程的生命周期
    • join():运行结束再下一个
    • yield():暂时让出cpu的使用权
    • deamon():守护线程,最后结束
    • sleep():如果有锁,不会让出
  • 三、线程的三大特性
    • 原子性:AtomicInteger
      • CAS
    • 可见性:加volatile关键字
    • 有序性:引用有了,对象还没
  • 四、集合中的线程安全问题
    • 1、List集合的问题
      • 用vector解决
      • 用Collections.synchronizedList
    • 2、表格总结
    • 3、关于hash算法
  • 总结

引出


1.线程创建的方式,继承Thread类,实现Runable接口,实现Callable接口,采用线程池;
2.线程生命周期: join():运行结束再下一个, yield():暂时让出cpu的使用权,deamon():守护线程,最后结束,sleep():如果有锁,不会让出;
3.线程3大特性,原子性,可见性,有序性;
4.list集合中线程安全问题,hash算法问题;

一、创建多线程的方式

1、继承Thread类

在这里插入图片描述

在这里插入图片描述

调用的流程:

  • 调用start()
  • 线程处于准备状态,一旦cup有闲的时间片,
  • 让线程调用run()方法

在这里插入图片描述

package com.tianju.threadLearn;

public class ThreadA extends Thread{

    @Override
    public void run(){
        System.out.println("我是线程A:"+this.getName());
    }

    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        threadA.setName("线程A");

        threadA.start(); // 我准备好了

        String name = Thread.currentThread().getName();
        System.out.println(name);

        // 获得当前线程的路径
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        System.out.println(path);

    }
}

当前线程:Thread.currentThread()

在这里插入图片描述

2、实现Runable接口

和继承Thread相比,这个用的更多,因为Java是单继承的,只能继承一个,而可以实现多个接口,所以更加灵活

在这里插入图片描述

package com.tianju.threadLearn;

/**
 * 这个常用,因为java是单继承,如果继承了extends Thread;
 * 就不能继续继承了;
 */
public class ThreadB implements Runnable{

    private String name;
    public ThreadB(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for(int i =0;i<10;i++){
            System.out.println("当前线程:"+this.name);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadB("B")).start();
    }
}

3、实现Callable接口

在这里插入图片描述

package com.tianju.threadLearn;


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

/**
 * Callable,可以有返回值
 */
public class ThreadC implements Callable<Long> {



    @Override
    public Long call() throws Exception {
        long sum = 0;
        for (int i = 0; i < 500000; i++) {
            sum+=i;
        }
        return sum;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Long> threadC = new FutureTask<Long>(new ThreadC());
        Thread thread = new Thread(threadC);
        thread.start();
        Long aLong = threadC.get();
        System.out.println(aLong);
    }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4、线程池

池化技术pool【常量池、数据连接池、线程池】

在这里插入图片描述

package com.tianju.threadLearn;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolDemo1 {
    public static void main(String[] args) {
        System.out.println("===============缓存线程池================");
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0;i<10;i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            });
        }
        System.out.println("===============固定线程池================");
        ExecutorService executorService2 = Executors.newFixedThreadPool(1);
        for (int i=0;i<10;i++) {
            executorService2.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            });
        }
    }
}

二、线程的生命周期

在这里插入图片描述

join():运行结束再下一个

在这里插入图片描述

package com.tianju.threadLearn;

public class ThreadA1 extends Thread{

    static int c = 0;

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {

            try {
                System.out.println(this.getName()+": "+i);
                sleep(300);
                c++;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadA1 a = new ThreadA1();
        a.setName("A");

        ThreadA1 b = new ThreadA1();
        b.setName("B");
        a.start();
        a.join(); // A运行结束,B才能开始运行

        b.start();
        b.join(); // 控制执行顺序

        System.out.println(Thread.currentThread().getName());
        System.out.println("c的结果为: "+c);

    }
}

在这里插入图片描述

yield():暂时让出cpu的使用权

在这里插入图片描述

package com.tianju.threadLearn;

/**
 * yield,让出cpu的使用权
 */
public class ThreadA2 extends Thread{

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {

            try {
                if (i%2==0){
                    // 让出cpu的使用权,避免一个线程一直占有cpu,防止独占cpu
                    // 重新竞争
                    yield();
                }
                System.out.println(this.getName()+": "+i);
                sleep(300);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadA2 a = new ThreadA2();
        a.setName("A");

        ThreadA2 b = new ThreadA2();
        b.setName("B");
        a.start();

        b.start();

        System.out.println(Thread.currentThread().getName());

    }
}

deamon():守护线程,最后结束

在这里插入图片描述

在这里插入图片描述

package com.tianju.threadLearn;

/**
 * 守护线程
 * 用户线程
 *
 */
public class ThreadA3 extends Thread{

    @Override
    public void run(){

        while (true){
            System.out.println("我是守护线程.......");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        ThreadA3 a = new ThreadA3();
        a.setName("A");
        a.setDaemon(true); // 守护线程,用户线程main结束后,他就结束了
        a.start();

        for (int i =0;i<10;i++){
            System.out.println(Thread.currentThread()+":"+i);
            Thread.sleep(200);
        }

    }
}

在这里插入图片描述

sleep():如果有锁,不会让出

在这里插入图片描述

package com.tianju.threadLearn;

public class ThreadSleep extends Thread{



    @Override
    public void run(){

        for (int i=0;i<10;i++){
            System.out.println("我是线程A:"+this.getName());

            try {
                sleep(200); // 不会让出锁
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public static void main(String[] args) {
        ThreadSleep threadA = new ThreadSleep();
        threadA.setName("线程A");

        threadA.start(); // 我准备好了

        String name = Thread.currentThread().getName();
        System.out.println(name);

    }
}

三、线程的三大特性

在这里插入图片描述

原子性:AtomicInteger

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.tianju.tx;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 解决原子性:AtomicInteger
 */
public class AtomicDemo1 {
    static AtomicInteger x= new AtomicInteger();
    static class A extends Thread{
        @Override
        public void run() {

            for (int i = 0; i < 20; i++) {
                x.incrementAndGet();
                System.out.println(getName()+ "--x:"+x);
                try {
                    sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
    }

    public static void main(String[] args) {
        new A().start();
        new A().start();
        new A().start();
    }
}

在这里插入图片描述

CAS

CAS(Compare And Swap)

  • 从主存拷贝到工作内存(线程)
  • 修改,和主存比较,如果如果和取时结果一致,刷新到主存中。

ABA问题:

如果红色线程拿到了主存中的3,进行加1,;

蓝色线程也拿到了主存中的3,进行了加1,然后又减1;

红色线程一直问主存里面是不是3,然后发现还是3,就把4写到主存里面;

但是此时的3已经被蓝色线程动过了;

解决方案是,加一个版本号,每次被操作,就让版本号加1

在这里插入图片描述

可见性:加volatile关键字

在这里插入图片描述

package com.tianju.view;

/**
 * 线程之间不可见
 * volatile 用于解决可见性
 */
public class VisibleDemo2 {
    volatile static boolean f = true; // 处理可见性,解决不了原子性
    static class A extends Thread{

        @Override
        public void run() {
            while (f){

            }
            System.out.println("A的f为:"+f);
        }
    }
    static class B extends Thread{
        @Override
        public void run() {
            f=false;
            System.out.println("B设置f为:"+f);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new A().start();
        Thread.sleep(300);
        new B().start();
    }
}

在这里插入图片描述

有序性:引用有了,对象还没

在这里插入图片描述

假设: 编译器修改顺序 1 , 3, 2

线程A执行到对象初始化阶段,还没有初始化;

线程B先获得的对象的引用,然后调用对象,

但是对象还没有初始化,因此会报错,对象初始化错误

在这里插入图片描述

四、集合中的线程安全问题

1、List集合的问题

在这里插入图片描述

在这里插入图片描述

package com.tianju.collection;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ListDemo {
    static List<Integer> list = new ArrayList<>();

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        Thread.sleep(500);
        a2.start();
        a3.start();
        System.out.println(list);
    }
}

用vector解决

在这里插入图片描述

package com.tianju.collection;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class VectorDemo {
    static Vector<Integer> list = new Vector<>();

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        a1.join(); // A运行结束,B才能开始运行
        a2.start();
        a2.join();
        a3.start();
        a3.join();
        System.out.println(list);
    }
}

用Collections.synchronizedList

package com.tianju.collection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class CollectionDemo {
    static List<Integer> ls = new ArrayList<>();
    static List<Integer> list = Collections.synchronizedList(ls);

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        a1.join(); // A运行结束,B才能开始运行
        a2.start();
        a2.join();
        a3.start();
        a3.join();
        System.out.println(list);
    }
}

在这里插入图片描述

2、表格总结

序号线程不安全线程安全
1ArrayListVector/ Collections.synchronizedList(list)
2HashMapConcurentHashMap/HashTable
3HashSetCollections.synchronizedSet()

Hashset的底层是HashMap

在这里插入图片描述

3、关于hash算法

比如一开始有蓝色点,经过hash算法之后,得到结果3,然后放到相应的位置;

然后又来了一个红色的点,经过hash算法之后,也是3,此时就顺着蓝色的位置去找;

之前是采用头插法,就是新来的放到蓝色的之前,在1.8之后改成尾插法,放到蓝色的后面;

如果又有新来的,就继续往后面加,此时出现了不利的情况,就是越来越多成了一个很长的链表;

所以就采用了树的结果,这样提高的查找的效率;在1.8中采用的红黑树;

在这里插入图片描述


总结

1.线程创建的方式,继承Thread类,实现Runable接口,实现Callable接口,采用线程池;
2.线程生命周期: join():运行结束再下一个, yield():暂时让出cpu的使用权,deamon():守护线程,最后结束,sleep():如果有锁,不会让出;
3.线程3大特性,原子性,可见性,有序性;
4.list集合中线程安全问题,hash算法问题;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【环境装配】Anaconda在启动时闪现黑框,闪几次后仍能正常使用,解决黑框问题

anaconda闪黑框这个问题遇到好久了&#xff0c;也没找到相关资料来解决&#xff0c;今天做了两个更新&#xff0c;刚好可以不闪黑框了&#xff0c;记录一下。 更新anaconda 在界面右上角的位置点击更新&#xff0c;更新完后再打开时只闪现两个黑框了&#xff0c;之前好像有五…

UE5 c++将自定义UserWdiget添加到对应菜单栏

前言&#xff1a; 为了实现与UserWidget一致的右键添加&#xff0c;便有了此章 注&#xff1a;这里使用的是UE5.3 目标内容&#xff1a; 这里可以参考UserWidget的源码&#xff0c;拷贝一份属于自己的就ok&#xff08;本篇章只是全改成了属于自己的CommonUserWidget&#xff…

定时发圈怎么设置?

微信本身是不能定时发送朋友圈的。微信公众号可以定时发送&#xff0c;微博可以定时发送&#xff0c;那微信可不可以也定时发送呢&#xff1f;当然可以&#xff0c;只要用这个方法&#xff0c;微信也能实现定时发朋友圈&#xff0c;不用再守着时间发朋友圈了。

USB HID在系统下通信的一些总结

前言 这篇文章主要介绍在PC&#xff08;上位机&#xff0c;Host&#xff09;端&#xff0c;通过HID与硬件进行通信的一些总结&#xff0c;像很多同学肯定和我一样压根不想 去了解什么USB相关的资料&#xff0c;毕竟USB太复杂了&#xff0c;只想有个API给我们进行下数据就好了&…

3D视觉引导纸箱拆码垛,助力物流行业转型升级

近年来&#xff0c;自动化和智能化技术在各行业的应用越来越广泛&#xff0c;特别是在物流和仓储领域。纸箱拆码垛是物流仓储中的一个重要环节。 人工分拣效率低、错误率高、成本高&#xff0c;传统的拆码垛设备存在兼容性差&#xff0c;对纸箱的识别率不高、操作不灵活等问题…

【分布式事务】初步探索分布式事务的概率和理论,初识分布式事的解决方案 Seata,TC 服务的部署以及微服务集成 Seata

文章目录 一、分布式服务案例1.1 分布式服务 demo1.2 演示分布式事务问题 二、分布式事务的概念和理论2.1 什么是分布式事务2.2 CAP 定理2.3 BASE 理论2.4 分布式事务模型 三、分布式事务解决方案 —— Seata3.1 什么是 Seata3.2 Seata 的架构3.3 Seata 的四种分布式事务解决方…

渗透实战靶机2wp

0x00 简介 1、测试环境 目标IP&#xff1a;10.xxxx 测试IP&#xff1a;192.168.139.128 测试环境&#xff1a;win10、kali等 测试时间&#xff1a;2021.7.22-2021.7.22 测试人员&#xff1a;ruanruan 2、测试过程 本次实战主要通过对收集到的端口、目录等信息进行持续整…

​怎么测试websocket接口

在部分业务中&#xff0c;我们需要使用长连接&#xff0c;我们可以使用http长连接或者websocket&#xff0c;开发结束后难免会遇到测试问题&#xff0c;这里推荐2个&#xff0c;一个是postman&#xff0c;一个是网站 postman 测试网站 测这边推荐测试网站&#xff0c;支持ws/w…

赴日工作赴日IT 如何找到一份日本IT工作?

IT在日本属于普通白领工作&#xff0c;那些想靠IT工作发财就不必考虑了。但是靠IT工作能安安稳稳的过个自己的小日子没问题&#xff0c;买房买车问题不大&#xff0c;作为一个普通人&#xff0c;在日本可以过的比较舒服。对有在日本长期发展的打算的还算是一个比较好的方向&…

Vue创建浅层响应式数据

shallowReactive&#xff1a;只处理对象第一层数据的响应式&#xff08;浅响应式&#xff09;。 shallowRef&#xff1a;只处理基本数据类型的响应式&#xff0c;不处理对象类型的响应式。 shallowReactive 适用于&#xff1a;如果有一个对象类型的数据&#xff0c;结构比较深…

【软考】2023下半年系统集成项目管理工程师案例分析真题(第五批次)

2023下半年系统集成项目管理工程师案例分析真题&#xff08;第五批次&#xff09; 案例一 (17分)-配置管理案例二 &#xff08;20分&#xff09;-进度管理案例三 &#xff08;18分&#xff09;-风险管理案例四 - 人力资源管理 系列文章版本记录 案例一 (17分)-配置管理 某游戏公…

【Spring】@Component组件

大前提&#xff1a; 添加了相关的约束文件以及注解支持 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:…

理解JavaScript模运算符

本文翻译自 Understanding the JavaScript Modulo Operator&#xff0c;作者&#xff1a;joshwcomeau&#xff0c; 略有删改。 当我第一次学习编码时&#xff0c;我记得发现模运算符&#xff08;%&#xff09;非常令人困惑。&#x1f62c; 当你不明白它的原理的时候&#xff0…

深度学习之基于YoloV5-Deepsort人物识别与追踪系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 YoloV5-Deepsort是一种基于深度学习的人物识别与追踪系统&#xff0c;具有较高的准确率和实时性能。 YoloV5是一种…

4.三大基础排序 -选择排序、冒泡排序、插入排序

排序算法 文章目录 冒泡排序算法步骤动图代码优化总结 选择排序算法步骤动图代码总结 插入排序算法步骤动图代码总结 排序算法&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。一般默认排序是按照由小到大即…

2.HTML中常用浏览器

2.常用浏览器 2.1 什么是浏览器 浏览器是网页显示&#xff0c;运行的平台。常用的浏览器有IE&#xff0c;火狐&#xff0c;谷歌&#xff0c;Safari和Opera等 平时成为五大浏览器 2.2 浏览器内核 浏览器内核&#xff08;渲染引擎&#xff09;&#xff1a;负责读取网页内容&…

用CMake编译项目 CMake和g++的区别

0.同样是编译c文件有了g为什么又出了个CMake&#xff1f; g适用于要编译的文件较少的情况。 而CMake像是写好的脚本&#xff0c;可以一键编译很多c文件。一.创建项目和CMakeLists.txt文件 1.1 创建一个项目&#xff0c;目录结构如下 1.2 在项目的根目录下创建一个CMakeLists.…

个人常用Linux命令

来自 linux命令学习-2023-8-1 153913.md等 1、切换目录 cd //切换目录 cd change directory cd 目录名 cd .. 返回上一级目录 pwd显示当前所处目录cd 绝对路径 cd ~ 表示一个用户的home目录 cd - 表示上一次访问的目录 cd / 表示进入根目录下//新建目录/data,并且进入/data…

001、Nvidia Jetson Nano Developer KIT(b01)-系统与登录

之——镜像烧录与远程登录 杂谈 Nvidia Jetson Nano Developer KIT&#xff08;b01&#xff0c;4G&#xff09;&#xff0c;系统配置全纪录&#xff0c;镜像烧录、系统安装、远程桌面安装、cuda与torch安装、pycharm、pycuda、tensorrt等等。 正文 1.开发板系统安装 1.1 开发…

创意涌动:CSDN·大学生博主14天创作挑战赛·第二期,正式开启报名!

文章目录 ⭐️ 活动介绍⭐️ 活动详情⭐️ 活动奖品⭐️ 活动流程⭐️ 评审规则⭐️ 报名&投稿注意事项⭐️ 关于活动官方 活动报名地址&#xff08;点击跳转&#xff09; 本次活动与官方活动及其他博主的创作型活动并不不冲突&#xff01; ⭐️ 活动介绍 亲爱的大学生博主…