Java学习记录(十九)多线程(一)

线程

线程是操作系统能进行调度的最小单位,他是被包含在进程中的,一个运行的软件可以看作为一个进程,而在该软件中执行的各种功能自身可以理解为一个线程,可以理解为在软件中互相独立又可以同时进行的功能,他是进程中实际的操作的单位

并发和并行

并发为在同一个cpu上同时多个指令交替进行(并发一个单位时间只处理一条指令,但是可以在这个单位时间内在多条指令之间跳转(也就是执行多个程序))

并行为在一个cpu上的多个线程或多个cpu上的多个线程中同时执行多个指令(并行一次处理多条指令)

开启线程

(一)第一种开启线程方法:继承Thread类

(该方法是在实现多线程的时候是在实现每个线程里的run方法,但run方法是没有返回值的,所以该种开启线程的方法适合在多线程执行任务时没有返回值的场景)

这种开启线程的步骤主要是使用strat方法去开启线程,首先需要一个需要开启线程的类去继承Thread类,并重写里面的run方法,run方法里面就是开启线程后需要执行的方法,再到其他类的main方法里面创建继承了Thread类该类的对象,通过该对象调用strat方法开启线程,具体如下:

package com.itazhang.Demo1;

public class MyTread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"helloWorld");
        }
    }
}
package com.itazhang.Demo1;

public class TreadDemo1 {
    public static void main(String[] args) {
        MyTread mt = new MyTread();
        MyTread mt2 = new MyTread();
        mt.setName("线程1");
        mt2.setName("线程2");
        mt.start();
        mt2.start();
    }
}

(二)第二种开启线程的方法:实现Runnable接口

(该方法是在实现多线程的时候是在实现每个线程里的run方法,但run方法是没有返回值的,所以该种开启线程的方法适合在多线程执行任务时没有返回值的场景)

这种方法是需要开启线程的类实现该Runnable接口,重写里面的run方法,方法体里的内容就是该线程具体需要执行的内容,然后在其他类里面创建该类的对象(相当于创建一个任务对象),再创建Thread类的对象,在创建Thread类对象的时候将之前创建的对象传递给这个对象(相当于将之前创建的任务交给这个线程),最后再通过start方法开启线程。

实现如下:在Mythread类中,调用了Thread类里的currentThread方法,该方法主要是在执行该线程时返回该线程,再通过该线程调用getName方法从而在该线程执行的时候得到该线程的名字

package com.itazhang.Demo2;

public class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"helloworld");
        }
    }
}
package com.itazhang.Demo2;

import com.itazhang.Demo1.MyTread;

public class Test {
    public static void main(String[] args) {
        //创建MyThread对象,相当创建一个任务
        MyTread m1 =new MyTread();
        MyTread m2 =new MyTread();
        m1.setName("线程1");
        m2.setName("线程2");
        Thread t1 =new Thread(m1);
        Thread t2 =new Thread(m2);
        t1.start();
        t2.start();
    }
}

(三)第三种开启方式:实现Callable接口和Future接口的方式进行开启线程

在需要线程的返回值时采用该方法

这种方法是通过需要开启多线程的类去实现callable接口,callable接口会有一个泛型,该泛型就表示call方法里面也就是线程需要执行的方法在执行后返回的数据类型,例如我在执行了这个线程也就是执行了这个线程内的call方法后要返回一个字符串,那么这个泛型就是String。重写里面的call方法,方法内部就是这个线程需要执行的内容,到其他类中创建这个实现了Callable接口的类对象(这个对象相当于线程中需要执行的任务)

创建Future接口的实现类Futuretask的对象f,在创建时将之前的任务传递给他,这个时候就能通过该接口的实现类对象调用里面的get方法获取到执行该任务的线程的返回值

最后再创建一个Thread类的对象,在创建时将实现了Future接口的实现类对象f传递给该线程Thread对象。开启线程,使用f的get方法获取到该线程的结果

实现代码如下:

package com.itazhang.Demo3;

import java.util.concurrent.Callable;

public class MyThread implements Callable<String> {

    @Override
    public String call() throws Exception {
        String str = "000111";
        return str;
    }

}
package com.itazhang.Demo3;

import com.itazhang.Demo1.MyTread;

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

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建任务
        MyThread m = new MyThread();
        //创建Future接口的实现类,通过该实现类的get方法得到上面实现该任务的线程的返回值
        FutureTask<String> f =new FutureTask<>(m);
        //创建线程,将该实现类对象交给该线程
        Thread t = new Thread(f);
        t.start();
        String s = f.get();
        System.out.println(s);

    }
}

线程内常用的成员方法 

前四种方法

在创建线程时不给线程设置名字,系统会自动的给线程设置默认名字,例如Thread0,如果使用继承Thread类的方式创建线程,且在创建该类对象的时候想直接传入线程的名字,那么在创建线程时需要重写该类的构造方法 ,如下:

package com.itazhang.Demo4;

public class MyThread extends Thread{
    public MyThread() {
        super();
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("aaa000");
        }

    }
}

同时,如果需要在该线程执行一次之后睡眠一段时间就可以调用Thread的静态方法sleep方法,该方法内部的参数是long类型,表示睡眠n毫秒,例如传入1000就是执行一次睡眠1秒,如下:下列代码就会在该线程内执行一次for循环就会睡眠一秒,再执行一次for循环,直到run方法执行完

package com.itazhang.Demo4;

public class MyThread extends Thread{
    public MyThread() {
        super();
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("aaa000");
        }

    }
}
package com.itazhang.Demo4;

import com.itazhang.Demo1.MyTread;

public class Test {
    public static void main(String[] args) {
        MyThread m = new MyThread("线程1");
        System.out.println(m.getName());
        m.start();
    }
}

线程的优先级

线程的优先级主要是表示的抢占到cpu的概率,线程的优先级大那么抢占到cpu的概率大,所以可以将一个线程的优先级设置大,这样该线程抢占cpu的概率就会变大

设置该线程的优先级主要是采用Thread类的setPriority,创建Thread相关对象调用该方法,传递进去1-10的数字,数字越大表明线程的优先级越高,抢占到cpu的概率越大,如果不设置优先级的话,线程默认的优先级数字是5。而获取到该线程的优先级,则是使用getpriority方法,实现代码如下:

package com.itazhang.Demo4;

import com.itazhang.Demo1.MyTread;

public class Test {
    public static void main(String[] args) {
        MyThread m = new MyThread("线程1");
        MyThread m1 = new MyThread("线程2");
        m.setPriority(1);
        m1.setPriority(10);
        m.start();
        m1.start();
    }
}

但是设置了线程的优先级只是增大了线程抢占cpu的概率,并不是一定能让该线程先抢占cpu执行完相关线程操作。

守护线程

守护线程是在其他非守护线程执行完毕后,就会自动缓缓的关闭该守护线程。也就是在其他非守护线程都执行完毕后,该守护线程也就没有存在的必要,会自动的慢慢关闭自己的线程。主要采用setDaemon方法将一个线程设置为守护线程,具体实现代码如下:

package com.itazhang.Demo4;

import com.itazhang.Demo1.MyTread;

public class Test {
    public static void main(String[] args) {
        MyThread m = new MyThread("线程1");
        MyThread m1 = new MyThread("线程2");
        m1.setDaemon(true);
        m.start();
        m1.start();
    }
}

插入线程

插入线程是将一个线程a调用join方法写入到另一个线程b中,这样的话线程b就会等待线程a执行完了之后才会执行,顾名思义相当于将线程a插入到了线程b的前面,具体实现代码如下:将m1插入到main方法中这时,main方法的线程会在m1线程执行完毕之后才会继续执行

package com.itazhang.Demo5;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread m1 = new MyThread();
        m1.start();
        for (int i = 0; i < 10; i++) {
            m1.join();
            System.out.println("我是main方法执行的线程");
        }
    }
}

线程的生命周期

线程的生命周期如下图,在创建线程时,会处于新建状态,而调用start方法后会让该线程去抢占cpu,如果抢占到cpu就会开始执行相关的代码,在执行期间如果调用了sleep方法或者其他阻塞方法就会等待并重新抢夺cpu,在抢夺到cpu之后继续执行相关代码,如果执行完毕,最后就会线程死亡,变为垃圾线程。

线程安全问题以及解决方案

同步代码块

在实际情况中,如果多个线程共同执行一个任务的话,任务里的固定数据应该用static修饰,表示该数据是所有线程所共享的,而在实现过程中由于线程的抢占问题,所以可能会导致在执行run方法时,多个线程随机执行,例如一个卖票系统在卖票的时候,多个窗口表示多线程进行卖票操作,但是在卖票时由于多个线程出现抢夺问题,(每次进行卖票的时候获取票号,如果同时获取,即票号一样,多个窗口进行卖票)例如在1窗口准备将序号为2的票出售时,这时2窗口也是准备将序号为2的票出售,这时1窗口没有抢占到cpu资源于是无法此时将该序列号的票售出,但是2窗口抢到了cpu资源将这张票售出了,在2窗口售出之后,1窗口抢占到了cpu资源但是由于手里的票号没有售出所以票号没有改变,这个时候窗口1就会自动的将票号为2的票售出,这个时候就会导致每个窗口所卖的票可能一致,运行截图如下:

这个时候就需要一个名为锁的机制,例如窗口1在卖序列号2的票的时候将这个窗口锁起来,让顾客只能在这个窗口买票,买完了再将锁打开,让这几个窗口再抢夺资源,谁抢到了cpu资源再将他锁起来,让顾客只能在那里买票,如此重复,就能避免多个窗口卖出重复的票号问题

在代码中实现是由synchronized关键字来实现,使用了该修饰符的代码块是一次只能被一个线程访问,轮流执行,将需要进行锁的代码放入synchronized修饰的代码块当中即可达到锁的目的,这个关键字修饰的锁代码块需要传递一个锁对象,这个锁对象必须是唯一的,也就是静态的对象,用该对象作为锁对象,如下:static Object obj = new Object();这里的obj对象就是创建的静态锁对象,用以下代码实现方式就能解决多进程的安全问题

但是这个锁的对象必须是唯一的,也就是必须得是静态的,如果不是唯一的,相当于每个线程拥有自己的锁,将一个线程锁上之后,另外一个线程也能执行相关代码(可以理解为一个房间两个门分别带有两把锁,一个线程进入之后将锁锁上,但是另外一个线程能通过另一扇门的锁来进入该房间,这样的话锁就没有意义了,所以一般使用当前类的字节码对象当作锁对象)

package com.itazhang.Demo;

import java.util.concurrent.BlockingDeque;

public class MyThread extends Thread{
    static int ticket = 0;
//    //创建的静态锁对象
//    static Object obj = new Object();
    @Override
    public void run() {
        while(true){
            //使用锁
            synchronized (MyThread.class){
                if(ticket<100){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket++;
                    System.out.println("这是卖的第"+ticket+"张票");
                }else if(ticket>=100){
                    break;
                }
            }
        }

    }
}

同步方法

将同步代码块里面的方法抽取出来用synchronized修饰变成同步方法,他的作用与同步代码块一样

下面为一个实列代码(注意:下面创建的为实现runnable接口的线程开启方式,里面的任务也就是ticket不用设置成静态变量,因为在测试类里创建线程时会将该任务传递给多个线程,所以在多个线程中该任务本来就是共享的,所以不用设置为静态变量

写同步方法主要有如下步骤:

1、写相关的功能循环

2、写如果满足条件执行的功能

3、如果不满足,停止循环并执行的功能

4、将synchronizaed修饰代码块添加到循环当中,将循环里需要锁的代码放入该同步代码块

5、再将该同步代码块抽取成方法,并将方法里的同步代码块删除,将synchronized修饰符添加到方法中,让方法成为同步方法

package com.itazhang.Demo2;

public class MyThread1 implements Runnable{
    int ticket = 1;
    @Override
    public void run() {
        while(true){
            if (extracted()) break;
        }
    }

    private synchronized boolean extracted() {
            if(ticket<100){
                System.out.println("这时卖的第"+ticket+"张票");
                ticket++;
            }else if(ticket>=100){
                return true;
            }
        return false;
    }
}

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

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

相关文章

js视频上传的方法

一、视频上传于图片上传类似他们的上传方法一样。路径不同标签不同&#xff1b; 二、直接上效果 三、直接上代码 // // 上传图片 let urls "https://wwz.jingyi.icu/"; let a $("form img") // console.log(a);function fl() {let read document.getE…

计算机网络面经八股-解释一下HTTP长连接和短连接?

在HTTP/1.0中&#xff0c;默认使用的是短连接。也就是说&#xff0c;浏览器和服务器每进行一次HTTP操作&#xff0c;就建立一次连接&#xff0c;但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其他的Web资源&#xff0c;如JavaScript文件、图…

探索制氮机在农产品保鲜中的应用方式

在现代生活中&#xff0c;农产品保鲜成为老生常谈的话题&#xff0c;水果数次厂商总是在为如何使水果蔬菜能够保存时间长一点而发愁&#xff0c;而制氮机的出现则解决了这一难题&#xff0c;为农产品保鲜技术带来了革命性的变革。本期恒业通小编和您一起了解制氮机在水果,蔬菜保…

k8s+wordpress+zabbix+elastic+filebeat+kibana服务搭建以及测试

一&#xff0c;环境&#xff1a;docker&#xff0c;k8s&#xff0c;zabbix&#xff0c;以及搭建worpdress&#xff0c;elasticsearch&#xff0c;filebeat&#xff0c;kibana 二&#xff0c;主机分配&#xff1a; 名称host详述个人博客3192.168.142.133 搭配mysql8.0.36的数据…

ubuntu安装并使用Anaconda

0、说明 对应着 Python 有 2.x 版本和 3.x 版本&#xff0c;Anaconda 也有 Anaconda2 以及 Anaconda 3 两个版本&#xff0c;考虑其流行度&#xff0c;一般谈及 Anaconda 时&#xff0c;默认为 Anaconda3。本人使用的ubuntu20.04。 1、Anaconda 简介 Anaconda 是一个用于科学…

【大模型API调用初尝试二】星火认知大模型 百度千帆大模型

大模型API调用初尝试二 科大讯飞—星火认知大模型单论会话调用多轮会话调用 百度—千帆大模型获取access_token单轮会话多轮会话 科大讯飞—星火认知大模型 星火认知大模型是科大讯飞开发的&#xff0c;直接使用可以点击星火认知大模型&#xff0c;要调用API的话在讯飞开发平台…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的交通信号灯识别系统(深度学习+UI界面+训练数据集+Python代码)

摘要&#xff1a;本研究详细介绍了一种采用深度学习技术的交通信号灯识别系统&#xff0c;该系统集成了最新的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地…

Xilinx 7系列FPGA的配置流程

目录 1.4配置流程 1.4.1 设备上电 ​编辑1.4.2 清除配置寄存器 1.4.3 采样模式引脚 1.4.4 同步 ​编辑1.4.5 检测设备ID ​编辑1.4.6 加载配置数据 1.4.7 CRC校验 1.4.8 启动序列 1.4配置流程 对于所有配置模式&#xff0c;7系列的基本配置流程都是相同的&…

算法练习:二分查找

目录 1. 朴素二分查找2. 在排序数组中查找元素的第一个和最后一个位置3. 搜索插入位置4. x的平方根5. 山脉数组的峰值索引6. 寻找峰值7. 寻找旋转排序数组中的最小值8. 点名 1. 朴素二分查找 题目信息&#xff1a; 题目链接&#xff1a; 二分查找二分查找的使用前提为数据具有&…

leetcode精选算法刷题训练篇 之 链表OJ(包含题目链接以及详细讲解)

好好学习&#xff0c;giao哥给你补&#x1f95a; 1、移除链表元素 难度等级&#xff1a;⭐ 题目链接&#xff1a;移除链表元素 2、链表的中间节点 难度等级&#xff1a;⭐⭐ 题目链接&#xff1a;链表的中间节点 3、反转链表 难度等级&#xff1a;⭐⭐⭐ 题目链接&#x…

C#版开源免费的Bouncy Castle密码库

前言 今天大姚给大家分享一款C#版开源、免费的Bouncy Castle密码库&#xff1a;BouncyCastle。 项目介绍 BouncyCastle是一款C#版开源、免费的Bouncy Castle密码库&#xff0c;开发人员可以通过该项目在他们的 C# 应用程序中使用 Bouncy Castle 提供的各种密码学功能&#x…

git提交代码描述时如何换行(更新时间24/3/12)

问题复现&#xff08;信心满满使用转义字符换行&#xff09; 解决方法&#xff1a; 写多个-m字符串的结构可以实现自动换行 注意空格 git commit -m"第一行描述" -m"第二行描述" 效果演示&#xff1a;&#xff08;强迫症福利&#xff09;

近700所高校,2024年预算出炉!

办学经费&#xff0c;是高校发展的核心与基石。学校人才培养、科学研究等各项事业的开展&#xff0c;都有赖于教育经费的支持。 近日&#xff0c;全国已有北京、上海、江苏、浙江等20多个省&#xff08;市、自治区&#xff09;的高校对外公布了2024年预算经费&#xff0c;小编…

L2-035 完全二叉树的层序遍历(Python)

L2-035 完全二叉树的层序遍历 分数 25 全屏浏览 切换布局 作者 陈越 单位 浙江大学 一个二叉树&#xff0c;如果每一个层的结点数都达到最大值&#xff0c;则这个二叉树就是完美二叉树。对于深度为 D 的&#xff0c;有 N 个结点的二叉树&#xff0c;若其结点对应于相同深度…

深入联合文件系统

Union File System&#xff08;联合文件系统&#xff0c;UnionFS&#xff09;是一种轻量级的高性能分层文件系统&#xff0c;它支持将文件系统中的修改信息作为一次提交&#xff0c;并层层叠加&#xff0c;同时可以将不同目录挂载到同一个虚拟文件系统下&#xff0c;应用看到的…

C# 8.0+版本项目 string不可为空

1.在某一次新建项目的时候发现&#xff0c;新建的项目&#xff0c;写的测试接口&#xff0c;接口的入参有string的参数&#xff0c; 但是调用接口的时候string的参数没有传报了400&#xff0c;很奇怪&#xff0c;也没有语法错误之类的。 2.解决办法 在项目上右键->属性->…

计算机毕业设计-springboot+vue前后端分离电竞社交平台管理系统部分成果分享

4.5系统结构设计 本系统使用的角色主要有系统管理员、顾客、接单员&#xff0c;本系统为后台管理系统&#xff0c;游客用户可以经过账号注册&#xff0c;管理员审核通过后&#xff0c;用账号密码登录系统&#xff0c;查看后台首页&#xff0c;模块管理&#xff08;顾客信息&am…

Covalent Network (CQT) 通过统一 API 集成,为 Gnosis Chain 的 AI 潜力赋能

作为一个为超 225 个链提供服务的领先多链索引器&#xff0c;Covalent Network (CQT) 正在与知名的 EVM 区块链基础设施提供者 Gnosis Chain 展开一项激动人心的合作。这一战略合作象征着先进的实时数据索引技术的集成&#xff0c;包括 Covalent Network (CQT) 的统一 API 和 G…

前端入职配置新电脑!!!

前端岗位入职第一天到底应该做些什么呢&#xff1f;又该怎样高效的认识、融入团队&#xff1f;并快速进入工作状态呢&#xff1f;这篇文章就来分享一下&#xff0c;希望对即将走向或初入前端职场的你&#xff0c;能够有所帮助。内含大量链接&#xff0c;欢迎点赞收藏&#xff0…

解决gpt无法发送对话的问题

问题描述 如图&#xff0c;今天登上去发现怎么无法发送消息 解决 可能是cookie问题&#xff0c;重新删除了就行了 cookie删除后&#xff0c;需要重新登录&#xff0c;主题色也重置为原来的白色了