Java小白教学—五千字带你了解多线程机制及线程安全问题

news2024/11/28 2:41:05

基础概念


 📖 问题一 : 什么是线程?线程和程序、进程有什么区别?

程序:为实现某种功能,使用计算机语言编写的一系列指令的集合。

           指的是静态的代码(例如安装在电脑上的那些文件)

进程:是运行中的程序(如运行中的王者荣耀)进程是操作系统进行资源分配的最小单位。

线程:进程可以进一步细化为线程,是进程中一个最小的执行单元,是cpu进行调度的最小单元

           例如:QQ中的一个聊天窗口

进程和线程的关系:

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

 •  一个线程只能隶属于一个进程. (QQ的聊天窗口只能属于QQ进程)

 •  每一个进程至少包含一个线程,也就是我们的主线程(像java中的main方法就是来启动主线程的)在主线程中可以创建并启动其他线程.

 •  一个进程的线程共享该进程的内存资源.


 📖 问题二 : 什么是多线程?多线程有哪些优缺点?

  ✎. 顾名思义多线程指:在一个程序中可以创建多个线程执行.

优点】 提高程序执行效率(多个任务可以在不同的线程中同时执行)

               提高了cpu的利用率

               改善程序结构,将复杂任务拆分成若干个小任务

缺点】 线程也是程序,线程越多占用内存也越多,cpu开销变大(可扩充内存或升级cpu)

             线程之间同时对共享资源的访问会相互影响,若不加以控制会导致数据出错.


 📖 问题三: 如何解决多线程操作共享数据的问题?

✎. 多个线程同时访问操作同一个共享的数据( 例如买票、抢购等 )时,可能会引起冲突,所以引入

线程 “同步” 机制,即各线程间要有先来后到。

即通过 【 排队+锁 】 在关键的步骤处,使多个线程只能一个一个的执行.

那么问题又来了,什么是锁呢?


锁机制(Lock)


📖 问题四: 什么是锁?锁有什么用?锁怎么用?

✎. 关于synchronized ( 同步锁 )   

语法结构:

   synchronized(同步锁对象) {
        同步代码块      
  }  

同步锁对象作用

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

同步锁对象要求:   同步锁对象必须是唯一 的。

✎. synchronized还可修饰方法.  

     synchronized修饰方法时,同步锁对象不需要我们指定,同步锁对象会默认提供:

  •     非静态方法 ------  默认是this
  •     静态方法   ------  锁对象是当前类的class对象 (一个类的对象只有一个)

✎. ReentrantLock与synchronized区别?

  • synchronized是一个关键字 ,控制依靠底层编译后的指令去实现.
  • synchronized可以修饰一个方法 或一个代码块.
  • synchronized是 隐式 的加锁和释放锁,一旦方法或代码块出现异常,会自动释放锁.

  • ReentrantLock是一个 ,依靠java底层代码去控制 (底层有一个同步队列)
  • ReentrantLock只能修饰 代码块.
  • ReentrantLock需要 手动 的加锁和释放锁, 所以释放锁最好写在finally中 , 一旦出现异常, 保证锁能释放.

误区:不是只要有线程就需要加锁,只有多个线程对同一资源共享时才加锁

创建线程的方式


 📖 问题五: 如何创建线程?有几种方式?

创建线程的方式通常有三种:

        •  通过继承Thread来创建线程

        •  通过实现Runnable接口来创建线程

        •  通过实现Callable接口来创建线程


📌 通过继承Thread来创建线程                                                                                                        

• 写一个类继承 java.lang.Thread

• 重写run( )

• 线程中要执行的任务都要写在run( )中,或在run( )中进行调用.

public class MyThread extends Thread{//继承Thread类
    @Override
    public void run() {//重写run方法
        for (int i = 1; i <= 200; i++) {
            System.out.println("run"+i);
        }
    }
}
 public static void main(String[] args) {
        //创建线程
        MyThread mythread = new MyThread();
        //启动线程
        mythread.start();
        for (int i = 1; i <= 200; i++) {
            System.out.println("main"+i);
        }
    }

 注意:

      启动线程调用的是start() ; 不是run()

      run()这不是启动线程,只是一个方法调用,没有启动线程,还是单线程模式的。

📖 Thread类中的方法:

run()    用来定义线程要执行的任务代码.
start()  启动线程
currentThread() 获取到当前线程(.得到具体信息)
setName()   为线程设置名字
getState()  获取状态
getPriority() setPriority 获取/设置 优先级
sleep() 让当前线程休眠指定时间.
join()  等待当前线程执行完毕,其他线程再执行.
yield() 主动礼让,退出cpu重新回到等待序列.

📖 关于优先级:

【java中默认优先级为5, 设置优先级范围为1~10】 ( 作用:为操作系统调度算法提供的 )


📌 通过实现Runnable接口来创建线程                                                                                             

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

• 重写任务执行的Run()

• 创建线程,并为线程指定执行任务.

public class MyThread implements Runnable {//实现Runnable接口
    @Override
    public void run() {//重写run方法
        for (int i = 0; i < 200; i++) {
            System.out.println("自定义线程");
        }
    }
}
public static void main(String[] args) {
        //创建任务
        MyThread mythread = new MyThread();
        //创建线程,并指定执行任务
        Thread thread = new Thread(mythread);
        thread.start();
    }

📖 实现Runnable接口创建的优点:

  •  因为java是单继承,一旦继承一个类就不能在继承其他类,避免单继承的局限。

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


📌 通过实现Callable接口来创建线程                                                                                               

 • 相比run( )方法,可以有返回值.

 • 方法可以抛出异常.

 • 支持泛型的返回值.

 • 需要借助FutureTask类,获取返回结果 

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

public class CallableDemo<T> implements Callable<T> {
    @Override
    public T call() throws Exception {//可以有返回值,也可以抛出异常
        Integer n = 0;
        for (int i = 0; i < 10; i++) {
            n += i;
        }
        return (T) n;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableDemo<Integer> callableDemo = new CallableDemo();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableDemo);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}

 这段代码是打印1~10的和,通过这个案例,相信你对实现Callable接口来创建线程已经大致了解

核心代码:

  CallableDemo<Integer> callableDemo = new CallableDemo();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableDemo);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());

线程生命周期


📖 问题六: 一个线程的生命周期是怎样的?

线程状态:

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

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

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

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

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

状态关系图:

模拟卖票案例: 两个窗口分别售票,票数为10张

public class MyThread extends Thread{//我们使用了继承Thread的方法
    static int num =10;   //票总数10,且为共享资源,要用static修饰
    static String obj = new String();//可以是任意类对象,但必须唯一。
/*  synchronized(同步锁对象) {
         同步代码块
    }                         */

    @Override
    public void run() {//线程要执行的代码块要写在run()中
        while (true){
            synchronized (obj){//加锁,一次只能执行一个线程
                if(num>0){
                    try {
                        Thread.sleep(800);//此处加入休眠为了让运行结果更明显,也可不加
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了第"+num+"张票");
                    num--;  //每抢一张票,总数num(10)就减1
                }else{
                    break;
                }
            }
        }
    }
}

在Main方法中创建线程并启动: 

 
public static void main(String[] args) {
        //创建两个线程,分别对应两个窗口
        MyThread myThread1 = new MyThread();
        myThread1.setName("窗口1");//线程1
        myThread1.start();

        MyThread myThread2 = new MyThread();
        myThread2.setName("窗口2");//线程2
        myThread2.start();
    }

运行结果: 

线程通信


📖 问题七: 什么是线程通信?怎么实现线程交替运行?

✎. 线程通信指:多个线程相互调度, 相互牵制,即线程间的相互作用.

wait( )   --- 让线程等待同时释放锁.

notify( ) --- 唤醒等待线程,必须写在同步代码块中进行,必须通过锁对象调用。

notifyAll( )--唤醒所有等待的线程.

这三个方法必须使用在同步代码块或同步方法中


✎. sleep( long time )与wait( )区别:

sleep ( ) :

  • 属于Thread类中的方法
  • sleep休眠指定时间后,会自动唤醒
  • sleep( ) 不会释放锁

wait ( ) :

  • 属于Object类中的方法,必须要有锁对象调用
  • wait后的线程必须要等待其他线程唤醒(notify或notifyAll)
  • wait( ) 自动释放锁

让我们通过一个例题来体会下线程通信吧!

两个线程交替打印1-100之间的数字

public class MyThread extends Thread{
    static int num = 1;
    static String string =new String();
    @Override
    public void run() {
        while (num<=100){
            synchronized (string){
                string.notify();

                System.out.println(currentThread().getName()+":"+num);
                num++;

                try {
                    string.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        MyThread mythread1 = new MyThread();
        mythread1.start();
        MyThread mythread2 = new MyThread();
        mythread2.start();
    }
}

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

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

相关文章

由于找不到msvcr120.dll,无法继续执行代码的详细处理方法,教你快修复msvcr120.dll

DLL文件&#xff0c;全称动态链接库文件&#xff0c;在计算机系统中具有重要作用。其中&#xff0c;msvcr120.dll是一个常见的DLL文件&#xff0c;它关联了许多程序和应用的正常运行。本指南将深入解释 msvcr120.dll文件的功能&#xff0c;并阐述如果缺少该文件会引起什么样的问…

C语言案例——输出以下图案(两个对称的星型三角形)

目录 图片代码 图片 代码 #include<stdio.h> int main() {int i,j,k;//先输出上半部图案for(i0;i<3;i){for(j0;j<2-i;j)printf(" ");for(k0;k<2*i;k)printf("*");printf("\n");}//再输出下半部分图案for(i0;i<2;i){for(j0;j&…

WebKit简介及工作流程

文章目录 一、WebKit简介二、WebKit结构三、Webkit工作流程四、WebKit常见问题五、WebKit优点六、相关链接 一、WebKit简介 WebKit是一个开源的浏览器引擎&#xff0c;它的起源可以追溯到2001年&#xff0c;当时苹果公司推出了其首款基于Unix的操作系统Mac OS X。在2002年&…

ST-LINK Utility 4.6.0 下载安装及使用方法介绍

一、介绍 STM32 ST-LINK Utility是针对STM32全系芯片进行编程&#xff08;读、写、擦除、选项字&#xff09;的一款工具。 STM32 ST-LINK Utility软件主要的功能就是量产&#xff08;批量下载代码的工具&#xff09;。它也是比较实用的一个工具&#xff0c;当我们需要查看芯片F…

BackTrader 中文文档(十)

原文&#xff1a;www.backtrader.com/ 用户自定义佣金 原文&#xff1a;www.backtrader.com/docu/user-defined-commissions/commission-schemes-subclassing/ 重塑 CommInfo 对象到实际形式的最重要部分涉及&#xff1a; 保留原始的 CommissionInfo 类和行为 为轻松创建用户定…

Android Material Design学习笔记

Material Design控件学习记录 Toolbar 新建一个工程后&#xff0c;在res/values/themes.xml文件里 <resources xmlns:tools"http://schemas.android.com/tools"><!-- Base application theme. --><style name"Theme.MaterialTest" paren…

【C++航海王:追寻罗杰的编程之路】异常——错误处理方式之一

目录 引言 1 -> C语言传统的处理错误的方式 2 -> C异常概念 3 -> 异常的使用 3.1 -> 异常的抛出和捕获 3.2 -> 异常的重新抛出 3.3 -> 异常规范 4 -> 自定义异常体系 5 -> C标准库的异常体系 6 -> 异常的优缺点 引言 在C编程中&#xff…

《剑指 Offer》专项突破版 - 面试题 110 : 所有路径(C++ 实现)

题目链接&#xff1a;所有路径 题目&#xff1a; 一个有向无环图由 n 个节点&#xff08;标号从 0 到 n - 1&#xff0c;n > 2&#xff09;组成&#xff0c;请找出从节点 0 到节点 n - 1 的所有路径。图用一个数组 graph 表示&#xff0c;数组的 graph[i] 包含所有从节点 …

javaWeb项目-游泳馆管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、SSM框架 开发信息…

机器学习实验------决策树

第1关&#xff1a;什么是决策树 任务描述 本关任务&#xff1a;根据本节课所学知识完成本关所设置的选择题。 第2关&#xff1a;信息熵与信息增益 任务描述 本关任务&#xff1a;掌握什么是信息增益&#xff0c;完成计算信息增益的程序设计。 import numpy as npdef calcIn…

3d视图模型乱了怎么调?---模大狮模型网

在进行3D建模时&#xff0c;有时候您可能会遇到视图模型混乱的情况。这可能是由于模型结构问题、导入导出错误或编辑操作不当等原因造成的。混乱的模型不仅影响工作效率&#xff0c;还可能导致渲染结果不理想。本文将介绍六种有效的方法来调整混乱的3D视图模型&#xff0c;帮助…

编程新手必看,Python3中函数知识点及语法学习总结(18)

介绍&#xff1a; Python3中的函数是组织好的、可重复使用的代码段&#xff0c;用于实现单一或相关联的功能。 以下是Python3中函数的一些基本介绍&#xff1a; 函数定义&#xff1a;在Python中&#xff0c;可以通过def关键字来定义一个函数。函数定义后&#xff0c;可以多次调…

从建表语句带你学习doris_表索引

1、doris建表概述 1.1、doris建表模板 CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [DATABASE.]table_name (column_definition1[,column_deinition2,......][,index_definition1,[,index_definition2,]] ) [ENGINE [olap|mysql|broker|hive]] [key_desc] [COMMENT "tabl…

【Shell语言学堂】sed命令最全详解

三剑客之sed linux sed命令详解1.简介2.定址3.sed选项4.正则表达式元字符5.具体应用1、行打印2、替换指定文本3、插入文本行&#xff0c;追加文本行4、sed命令的删除功能5、其他命令6、sed 的分组替换功能7、sed -r或者sed -E linux sed命令详解 CSDN划过手的泪滴t 1.简介 se…

L2-1 堆宝塔分数

本题链接&#xff1a;PTA | 程序设计类实验辅助教学平台 题目&#xff1a; 样例&#xff1a; 输入 11 10 8 9 5 12 11 4 3 1 9 15 输出 4 5 思路&#xff1a; 这是一道模拟题&#xff0c;需要有耐心读题&#xff0c;跟着题目走一遍&#xff0c;就可以了。 代码详解如下&…

润乾报表平台 InputServlet 任意文件读取漏洞复现

0x01 产品简介 润乾报表是一个纯JAVA的企业级报表工具&#xff0c;支持对J2EE系统的嵌入式部署&#xff0c;无缝集成。服务器端支持各种常见的操作系统&#xff0c;支持各种常见的关系数据库和各类J2 EE的应用服务器&#xff0c;客户端采用标准纯html方式展现&#xff0c;支持…

基于SSM的游戏攻略管理系统

游戏攻略管理系统的构建与实现 一、系统概述二、系统架构与技术选型三、系统功能模块四、系统特点五、总结与展望 随着网络游戏的普及和发展&#xff0c;游戏攻略成为玩家们提升游戏技能、了解游戏机制的重要途径。为了更好地满足玩家需求&#xff0c;提高游戏攻略的管理效率和…

Xinstall:实现注册后自动绑定,提升用户体验

在移动互联网时代&#xff0c;App的注册与绑定流程对于用户体验至关重要。繁琐的注册步骤和手动绑定操作往往会让用户望而却步&#xff0c;导致用户流失。为了解决这一问题&#xff0c;Xinstall品牌推出了注册后自动绑定功能&#xff0c;极大提升了用户体验。 Xinstall的自动…

优惠券布局的最终方案------css属性mask

先贴图&#xff1a; 以上这些都是通过mask去实现出来&#xff1a; <!DOCTYPE html><html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…

实战小项目 | ESP32-S3和ESP32-C3通过ESP-Mesh-Lite组网 温湿度传感器案例

传统Wi-Fi网络所有终端设备都需要直接与路由器相连&#xff0c;这使得Wi-Fi的覆盖区域受到路由器位置的限制&#xff0c;可接入终端设备的数量也受到路由器容量的限制。而乐鑫ESP-Mesh-Lite Wi-Fi组网方案&#xff0c;所有终端设备都可以与相邻设备连接&#xff0c;摆脱了对路由…