多线程同步处理方式(synchronized)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

实现多线程同步的推荐方法是同步方法,其语法形式如下:

  1. 访问修饰符 synchronized 返回类型 方法名{
  2. //同步方法体内代码块
  3. }

synchronized介绍

每个类实例都对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞。

synchronized 方法一旦执行,就独占该锁,直到该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入就绪状态。

这种机制确保了同一时刻对于每个类实例,其所有声明为 synchronized 的方法中至多只有一个处于就绪状态,从而有效避免了类成员变量的访问冲突。

image.png

小知识:同步有两种方式都制约,分间接和直接。间接主要来源于资源共享,也就是互斥访问资源;直接来源于进程的合作,比如生产者消费者问题。

静态方法是所有类实例对象所共享的,所以所有线程对象在访问此静态方法时是互斥访问的,从而实现线程的同步。

背景:

  • 线程和进程

在操作系统中,使用进程是为了使多个程序能并发执行,以提高资源的利用率和系统吞吐量。在操作系统中再引入线程,则是为了减少系统开销,使计算机操作系统具有更好的并发性。

进程是资源分配的基本单位,所有与该进程有关的资源,线程属于某一个进程,并与进程内的其他线程一起共享进程的资源。

线程是操作系统中的基本调度单元,所以每个进程在创建时,至少需要同时为该进程创建一个线程,线程也可以创建其他线程。

  • 单线程与多线程

打开计算机,可以同时运行很多程序,比如一边挂着 QQ,一边玩原神,能够做到这样是因为一个操作系统可以同时运行多个程序。一个正在运行的程序对于操作系统而言称为进程。

程序和进程的关系可以理解为,程序是一段静态的代码,是应用程序执行的蓝本,而进程是指一个正在内存中运行的程序,并且有独立的地址空间。

线程和进程一样拥有独立的执行路径,二者的区别在于,线程存在于进程中,拥有独立的执行堆栈和程序计数器,但没有独立的存储空间。一个线程会和所属进程中的其他线程共享存储空间。

传统的程序,一个进程里只有一个线程,所以也称为单线程程序,而多线程程序是一个进程里拥有多个线程,两者间的结构区别如下图所示:

图片描述

  • 线程的三大优势

介绍线程对于进程的优势,只有理解了采用线程比采用进程的好处才能更好地理解多线程的优势。

  1. 系统开销小
  1. 方便通信和资源共享

    如果是在进程之间通信,往往要求系统内核的参与,以提供通信机制和保护机制。而线程间通信在同一进程的地址空间内,共享主存和文件,操作简单,无须系统内核参与。

  2. 简化程序结构

    用户在实现多任务的程序时,采用多线程机制实现,程序结构清晰,独立性强。

线程的创建和使用

  • 线程的 5 种状态,以及线程的基本流程。
  • 线程类的创建有两种方式,创建一个类可以继承 Thread 类,或是实现 Runnable 接口。
  • 线程需要做的事情,主要是写在线程体 run() 方法中,调用 Thread 类中的 start() 方法启动线程。

如果线程类直接继承 Thread 类,其结构如下:

  1. [public] class 类名 extends Thread{
  2. //属性
  3. //其他方法
  4. public void run() { // 重写 Thread 类中的 run() 方法
  5. //线程需要执行的核心代码
  6. }
  7. }

如果线程类是实现 Runnable 接口的,其结构如下:

  1. [public] class 类名 implements Runnable{
  2. //属性
  3. //其他方法
  4. public void run() { // 实现 Runnable 接口中的 run() 方法
  5. //线程需要执行的核心代码
  6. }
  7. }

👀线程是同一时刻运行多个程序,进程是一段时间运行多个程序,进程是宏观并行,微观串行。

下面是用synchronized锁实现同步:

  • 1.写线程类
    假设对线程输出打印互斥访问
    1. class SyncThread implements Runnable {
    2. private int tid;
    3. public SyncThread2(int id) {
    4. this.tid = id;
    5. }
    6. public void run() {
    7. doTask(this.tid);
    8. }
    接下来完成doTask。
    1. //通过类的静态方法实现互斥访问
    2. private static synchronized void doTask(int tid) {
    3. for (int i = 0; i < 10; i++) {
    4. System.out.println("线程ID名为: " + tid + "正在输出:" + i);
    5. }
    6. }

main函数这样写
```public static void main(String[] args) {

for (int i = 0; i < 5; i++) {

new Thread(new SyncThread(i)).start();

}

}

  1. 这一句new Thread(new SyncThread(i)).start(); 也可以写成

Thread t1=new SyncThread();

t1.start();
```
这里t1是随便定义的名字。


文章标签:

原文连接:https://juejin.cn/post/7110854996366721037

相关推荐

云徙科技CTO李楠:技术和技术人,与数字化共同进化

Java通过反射注解赋值

Java开发学习(十五)----AOP入门案例及其工作流程解析

2022-07-21 第四组 java之继承

JavaScript进阶内容——BOM详解

注意 new BigDecimal(double val) 的使用

创建私有CA,我就用openSSL

JavaScript必须掌握的四大基础知识(数据类型、原型和原型链、闭包、异步 promise)

百度APP Android包体积优化实践(二)Dex行号优化

814. 二叉树剪枝 : 简单递归运用题

【综合笔试题】难度 3.5\u002F5,多解法热门二叉树笔试题

【java刷算法】牛客—剑指offer3栈、数组、递归、二分法的初步练习

JavaScript中一些流行的模块、vite快速搭建、vue3简梳理

JavaScript进阶内容——DOM详解

JavaScript基础系列(6):`this`这六种使用方式

【跟着大佬学JavaScript】之数组去重(结果对比)

Java开发学习(十四)----Spring整合Mybatis及Junit

Java代码优化的30个小技巧

JavaScript 中的单例内置对象(Global & Math)

JavaScript:聊聊作用域、作用域链与它的一些优化