Java——线程

程序

为实现某些功能,使用计算机语言编写的一系列指令(代码)的集合

特指静态的,安装在硬盘中的代码集合

进程

运行中的程序(被加载到内存中),是操作系统进行资源分配的最小单位

线程

进程可以进一步细化为线程,是进程内一个最小执行单元(具体要做的事情)

是cpu进行任务调度的最小单位

线程属于进程

例如:运行的qq就是一个进程,操作系统会为这个进程分配内存资源

一个聊天窗口就认为是一个线程,多个聊天窗口可以同时被cpu执行,这些聊天窗口也属于进程

早期没有线程时,CPU是以进程为单位执行的

但进程单位还是比较大的,当一个进程运行时,其他的进程就不能执行

所以后来将进程中的多个任务细化为线程,cpu执行单位,也从进程转为更小的线程    

main方法Java中的主线程,按从上到下的顺序执行

进程和线程的关系

一个进程可以包含多个线程(一个QQ可以有多个聊天窗口)

一个线程只能隶属于一个进程,线程不能脱离进程存在(QQ聊天窗口只能属于QQ)

一个进程至少有一个一个线程(主线程,java中的main方法就是用来启动主线程的)

在主线程中可以创建并启动其他线程

所有线程都共享该进程的内存资源

需求

想要在Java程序中有几件不相关的事情同时有机会执行

可以在Java中创建线程,把一些要执行的任务放在线程中执行

这样的话,这些任务都拥有让cpu执行的权力

Java中创建线程

方式1

一个类继承java.lang.Thread   重写run()

public class MyThread extends Thread{
    //run方法是线程执行任务的方法
    @Override
    public void run() {
        this.test();
    }
    public void test(){
        for (int i = 0; i < 100; i++) {
            System.out.println("MyThread"+i);
        }
    }
}

创建并启动线程

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
        }
    }
}

注意:myThread.run()并不是启动线程,而是普通的方法调用,还是单线程模式的;start()才是启动线程的

方式2

只先创建线程要执行的任务,创建一个类,实现Runnable接口  重写任务执行的run()

public class Task implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("自定义线程"+i);
        }
    }
}

创建并启动线程

public class ThreadTest1 {
    public static void main(String[] args) {
        //创建任务
        Task task = new Task();
        //创建线程,并为线程指定执行任务
        Thread thread = new Thread(task);
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
        }
    }
}

实现Runnable接口创建的优点:

  1.因为java是单继承,一旦继承一个类就不能再继承其他类,避免单继承的局限性

  2.适合多线程来处理同一份资源时使用

方式3

实现Callable接口,重写call()方法,call()可以有返回值,可以抛出异常,还可以自定义返回值结果的类型

import java.util.concurrent.Callable;

public class SumTask<T> implements Callable<T> {
    @Override
    public T call() throws Exception {
        Integer i = 0;
        for (int j = 0; j < 10; j++) {
            i+=j;
        }
        return (T)i;
    }
}

创建并启动线程

public class Test {
    public static void main(String[] args) {
        SumTask<Integer> sumTask = new SumTask<>();
        FutureTask<Integer> futureTask = new FutureTask(sumTask);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Integer i = futureTask.get();//获取到线程的执行结果
            System.out.println(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

常用方法

run();

        用来定义线程要执行的任务代码

start();

        启动线程

currentThread();

        获取当前线程

​System.out.println(Thread.currentThread());
//输出Thread[Thread-0,5,main]

getId();

        Thread.currentThread().getId();获取线程id

getName();

       Thread.currentThread().getName();获取线程名字

setName();

        myThread.setName("线程1"); 为线程设置名字

getPriority();

        Thread.currentThread().getPriority();获取优先级

setPriority();

        myThread.setPriority(10);

        设置线程优先级  优先级为1-10 默认是5  作用:为操作系统调度算法提供的

getState();

        Thread.currentThread().getState();获取线程状态

sleep(long milis);

        让线程阻塞、休眠指定的时间

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

yield();

        当前线程主动礼让,退出cpu,让下一个线程执行

for (int i = 0; i < 100; i++) {
    Thread thread = Thread.currentThread();//获取当前正在执行的线程
    if(i%10==0){
        Thread.yield();
    }
    System.out.println(thread.getName()+":"+i);
}

join();

        等待调用了join()线程执行完毕,其他线程再执行

public class Test {
    public static void main(String[] args){
        MyThread myThread1 = new MyThread();
        myThread1.start();
        myThread1.join();//myThread1执行完毕,myThread2才能执行
        MyThread myThread2 = new MyThread();
        myThread2.start();
    }
}

wait();

        让线程等待,自动释放锁,必须要其他线程唤醒

notify();

        唤醒等待中的线程(调用了wait()的线程),如果有多个等待,唤醒优先级高的

notifyAll();

        唤醒所有等待的线程

这三个方法都是Object类中定义的方法,必须在同步代码块中使用,必须通过锁的对象调用

while (true){
    synchronized (obj){
        obj.notify();//唤醒等待中的线程
        if(num<100){
            num++;
            System.out.println(Thread.currentThread().getName()+num);
        } else {
            break;
        }
        try {
            obj.wait();
            //让线程等待,同时释放了锁,等待的线程不能自己醒来,必须让另一个线程唤醒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
wait()与sleep()的区别

wait()是Object类中的方法,必须在同步代码块中使用,wait()后会释放锁,要用锁对象调用,必须等待其他线程唤醒(notify()或notifyAll())

sleep()是Thread类中的方法,在任何地方都能使用,sleep()不会释放锁,在休眠指定时间后,就会恢复

线程生命周期(生老病死   创建--销毁)

线程状态:

    新建:刚刚创建了一个线程对象,并没有启动

    就绪(可运行):调用start()后,线程就进入到了就绪状态,进入到了操作系统的调度队列

    运行状态:获得了cpu执行权,进入到cpu执行

    阻塞状态:例如调用了sleep(),有线程调用join(),线程中进行Scanner输入……

    死亡/销毁:run()中的任务执行完毕了

多线程

在一个程序中可以创建多个线程执行    

多线程优点:

1.提高程序执行效率    多个任务可以在不同的线程中同时执行

2.提高了cpu的利用率

3.改善程序结构,例如将一个的任务拆分成若干个小任务

多线程缺点:

1.线程多了占用内存

2.cpu开销就变大了(扩充内存,升级cpu)        

3.多个线程访问操作同一个共享的数据(例如买票,抢购等),会出现线程安全问题

如何解决多线程操作共享资源

排队+锁  在关键的步骤,多个线程只能一个一个的执行        

加锁方式1

使用synchronized关键字修饰代码块和方法

修饰代码块

同步对象要求:多个线程用到的必须是同一个对象,可以是java中任何类的对象

作用:用来记录有没有线程进入到同步代码块中,如果有线程进入到同步代码块,那么其他线程就不能进入直到上一个线程执行完同步代码块的内容,释放完锁之后,其他线程才能进入

synchronized(同步对象/同步锁){

        同步代码块,一次只允许一个线程进入

}

1.继承Thread

​​public class TicketThread extends Thread{
    int num = 10;//模拟有十张票
    static String obj = new String();//同步锁对象
    @Override
    public synchronized void run() {
        while (true){
            synchronized (obj){
                if(num>0){
                    System.out.println(Thread.currentThread().getName()+"买到了第"+num+"票");
                    num--;
                } else {
                    break;
                }
            }
        }
    }
}

2.实现Runnable接口

​
​​public class TicketThread extends Thread{
    int num = 10;
    @Override
    public synchronized void run() {
        while (true){
            synchronized (this){//this只有一个
                if(num>0){
                    System.out.println(Thread.currentThread().getName()+"买到了第"+num--+"张票");
                } else {
                    break;
                }
            }
        }
    }
}

​
修饰方法

1.锁不需要我们提供,会默认提供锁对象

2.synchronized如果修饰的是非静态的方法,锁对象是this

public class Ticket implements Runnable{
    int num = 10;
    @Override
    public void run() {
        while (true){
            if(num>0){
                this.printfTicket();
            }
            else break;
        }
    }
    public synchronized void printfTicket(){
        if(num>0){
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买到了第"+num--+"张票");
        }
    }
}

synchronized如果修饰的是静态的方法,锁对象是类的Class对象

一个类只有一个Class对象

​​​public class TicketThread extends Thread{
    static int num = 10;
    @Override
    public void run() {
        while (true){
            if(num<=0){
                break;
            }
            TicketThread.printf();
        }
    }
    public static synchronized void printf(){
        if(num>0){
            System.out.println(Thread.currentThread().getName()+"买到了第"+num--+"张票");
        }
    }
}
加锁方式2

使用jdk中提供的ReentrantLock类实现加锁

ReentrantLock只能对某一段代码块加锁,不能对整个方法加锁

reentrantLock.lock();      加锁

reentrantLock.unlock();  释放锁

import java.util.concurrent.locks.ReentrantLock;

public class TicketTask implements Runnable{
    int t = 10;
    ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                reentrantLock.lock();//加锁
                if(t>0){
                    System.out.println(Thread.currentThread().getName()+"买到了第"+t--+"张票");
                }
                else {
                    break;
                }
            }finally {//可能出现异常,因此在finally块中保证锁释放
                reentrantLock.unlock();//释放锁
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        TicketTask ticket = new TicketTask();
        Thread t1 = new Thread(ticket,"窗口1");
        Thread t2 = new Thread(ticket,"窗口2");
        t1.start();
        t2.start();
    }
}
synchronized 和 ReentrantLock区别

相同点:都实现了加锁的功能

不同点:

synchronized是一个关键字,ReentrantLock是一个类

synchronized修饰代码块和方法,ReentrantLock只能修饰代码块

synchronized可以隐式的加锁和释放锁,运行过程中如果出现异常可以自动释放

ReentrantLock需要手动的添加锁和释放锁,建议在finally代码块中释放锁,一旦出现异常,保证锁能释放

线程间的通信(在同步代码块的基础上,使用wait,notify对线程进行控制)

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

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

相关文章

运费计算(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int distance, c;float weight, price, discount, fee;//提示用户&#xff1b;printf("请输入路程&#xff0c;单价…

自动驾驶时代的物联网与车载系统安全:挑战与应对策略

随着特斯拉CEO埃隆马斯克近日对未来出行景象的描绘——几乎所有汽车都将实现自动驾驶&#xff0c;这一愿景愈发接近现实。马斯克生动比喻&#xff0c;未来的乘客步入汽车就如同走进一部自动化的电梯&#xff0c;无需任何手动操作。这一转变预示着汽车行业正朝着高度智能化的方向…

C语言基础---指针的基本语法

概述 内存地址 在计算机内存中&#xff0c;每个存储单元都有一个唯一的地址(内存编号)。通俗理解&#xff0c;内存就是房间&#xff0c;地址就是门牌号 指针和指针变量 指针&#xff08;Pointer&#xff09;是一种特殊的变量类型&#xff0c;它用于存储内存地址。指针的实…

六、数据呈现

目录 6.1 理解输入输出 6.1.1 标准文件描述符 1 STDIN &#xff08;0&#xff09; 2 STDOUT &#xff08;1&#xff09; 3 STDERR&#xff08;2&#xff09; 6.1.2 重定向错误 1 只重定向错误 2 重定向错误和数据 6.2 在脚本中重定向输出 6.2.1 临时重定向 6.2.2 永…

腾讯云全新云服务器实例S8/M8正式上线,性能提升高达115%!

2024年4月15日&#xff0c;腾讯云正式上线第八代云服务器标准型实例 S8和内存型实例M8。基于自研服务器的高密设计与硬件升级&#xff0c;搭载第五代英特尔至强可扩展处理器的腾讯云实例S8/M8&#xff0c;计算性能大幅提升&#xff0c;对比腾讯云云服务器上代实例&#xff0c;整…

Ubuntu 22.04安装中文输入法

1. 安装 sudo apt install fcitx5 2. 管理已安装的语言 Setting->Region & Language->Manage Installed Language 在下图中点击“安装”&#xff0c;之后需要等一会 选择Fcitx 5 3. 添加输入法 Setting->Keyboard 点击chinese 选择你想要的输入法 重启一下&a…

React + 项目(从基础到实战) -- 第七期

使用ant design 表单组件,开发登录,注册,搜索功能 React 表单组件 ,受控组件 案列 使用defaultVlue属性 bug : 改变了数据源,但是页面未重新渲染 {/* 表单组件 */}<button onClick{()>{console.log(text);}}>打印</button><button onClick{()>[setText(&…

【网络编程】Linux网络内核结构以及分布剖析

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之Linux网络内核结构以及分布剖析&#xff0c;在这篇文章中&#xff0c;你将会学习到在Linux内核中如何实现网络数据的输入和输出的&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助…

《TinyLlama: An Open-Source Small Language Model》全文翻译

【Title】 TinyLlama&#xff1a;开源小语言模型 【Abstract】 我们推出了 TinyLlama&#xff0c;这是一个紧凑的 1.1B 语言模型&#xff0c;在大约 1 万亿个令牌上进行了大约 3 个时期的预训练。 TinyLlama 基于 Llama 2&#xff08;Touvron 等人&#xff0c;2023b&#xff…

【Python】#3 基本程序控制

文章目录 一、分支结构if语句&#xff08;Python没有switch&#xff09;tips:紧凑形式&#xff1a;适用于简单表达式的二分支结构 二、循环结构1. for语句range函数 2. while语句3. 循环保留字&#xff1a;break/continue与带else的循环语句 一、分支结构if语句&#xff08;Pyt…

关于伴行天使车载监护器的技术路线

为了判断分析并反馈孩童是否昏睡状态&#xff0c;以预防因分心后排而导致的交通事故&#xff0c;本团队根据基于回归树对齐算法中获取的数据&#xff0c;建立了疲劳度评分机制。 本评分机制采用人脸关键点智能标注模型检测人脸&#xff0c;通过人脸识别68特征点检测、分别获取…

16.读取指定路径下的txt文档然后合并内容为一个txt文档。

1.题目要求 分别读取路径为 ./middle/phone/base/1_student_0.txt, ./middle/vr/base/1_teacher.txt, ./nearby/phone/base/1_student_0.txt, ./nearby/vr/base/1_teacher.txt, ./outside/phone/base/1_student_0.txt, ./outside/vr/base/1_teacher.txt 里面的文件&#xff…

【MATLAB源码-第43期】基于matlab的turbo码误码率仿真比较不同迭代次数,采用logmap/sova算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 Turbo码是一种前向纠错码 (Forward Error Correction, FEC)&#xff0c;在 1993 年由法国的两位研究员 Claude Berrou 和 Alain Glavieux 提出。这种编码技术以其接近 Shannon 极限的高性能而受到广泛关注。以下是关于 Turbo…

【word】文档标题如何自动编号

我在写一个word文档的时候&#xff0c;每一级标题的格式都设置好了&#xff0c;包括字体&#xff0c;大小等等&#xff0c;但是如何自动编号呢&#xff1f; 在写中期报告的时候&#xff0c;我对每一级标题的格式都创建了一个单独的样式&#xff0c;像这样&#xff1a; 对于每一…

Linux——信号量与基于环形队列的生产者消费者模型

目录 前言 一、信号量 二、信号量的接口 1.初始化 2.销毁 3.申请信号量 4. 释放信号量 三、基于环形队列的生产者消费者模型 1.环形队列的理解 2.生产者消费者的设计 3.单消费者单生产者环形队列的实现 4.多消费者多生产者环形队列的实现 前言 之前&#xff0c;…

AI的说服力如人类?Anthropic最新研究揭秘机器的辩论能力|TodayAI

人们常常对人工智能模型在对话中的说服力表现持怀疑态度。长久以来&#xff0c;社会上一直存在一个疑问&#xff1a;人工智能是否会达到人类那样&#xff0c;在对话中具有改变他人想法的能力&#xff1f; 直到最近&#xff0c;这一领域的实证研究相对有限&#xff0c;对于人工…

HTML5 新增语义标签及属性

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍HTML5 新增语义标签及属性&#x1f48e;1 HTML5 新增的块级语义化标签&…

海外代理IP在跨境电商中发挥什么作用?

在我国跨境电商的发展中&#xff0c;海外代理IP的应用日益广泛&#xff0c;它不仅帮助商家成功打入国际市场&#xff0c;还为他们在多变的全球电商竞争中保持优势。下面是海外代理IP在跨境电商中五个关键的应用场景。 1、精准的市场分析 了解目标市场的消费者行为、产品趋势以…

pyinstaller工具打包python项目详细教程

使用 Pyinstaller工具 编译打包 Python 项目生成 exe 可执行文件 1.pyinstaller介绍&#xff1a; 介绍&#xff1a;PyInstaller 是一个将 Python 程序转换为独立可执行文件的工具。它能够在 Windows、Linux、Mac OS X、AIX 和 Solaris 等多种系统上运行。详细介绍可参考pyins…

记录--病理切片图像处理

简介 数字病理切片&#xff0c;也称为全幻灯片成像&#xff08;Whole Slide Imaging&#xff0c;WSI&#xff09;或数字切片扫描&#xff0c;是将传统的玻片病理切片通过高分辨率扫描仪转换为数字图像的技术。这种技术对病理学领域具有革命性的意义&#xff0c;因为它允许病理…