1501 - JUC高并发

news2024/11/25 15:53:48

须知少许凌云志,曾许人间第一流

看的是尚硅谷的视频做的学习总结,感恩老师,下面是视频的地址

传送门icon-default.png?t=N7T8https://www.bilibili.com/video/BV1Kw411Z7dF

0.思维导图

1.JUC简介

1.1 什么是JUC

JUC, java.util.concurrent工具包的简称,一个处理线程的工具包。

1.2 进程和线程的概念

1.2.1 进程与线程

  • 进程

指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是资源分配的最小单位。

  • 线程

系统分配处理器时间资源的基本单元;进程之内独立执行的一个单元执行流;线程是程序执行的最小单位。

一个进程包含多个线程。

1.2.2 线程的状态

查看jdk源码

1.2.3 wait 和 sleep

区别

  1. sleep是Thread的静态方法;wait是Object的方法,任何对象实例都能调用。
  2. sleep不会释放锁,它也不需要占用锁;wait会释放锁。(wait会释放锁去睡觉,sleep会抓住锁去睡觉,在哪里谁就会在哪里醒)
  3. 它们都可以被interrupt方法中断。

1.2.4 并发和并行

  • 并发

同一时间间隔内多个线程交替执行,实际上是宏观上并行,微观上串行。(春运抢票,电商秒杀,抢同一个资源

  • 并行

同一时刻多个线程正在执行,多核并行。(一边看书,一边听音乐)

1.2.5 管程

叫 Monitor 监视器,就是锁。

是一种同步机制,保证同一个时间,只有一个线程访问被保护的数据或者代码。

1.2.6 用户线程和守护线程

  • 用户线程

自定义的线程,不随主线程的结束而结束。主线程结束了,用户线程还会运行,jvm还是存活状态。

  • 守护线程

随着主线程的结束而结束,如垃圾回收线程。主线程结束,jvm结束。

2.Lock接口

2.1 Synchronized

2.1.1 Synchronized作用范围

synchronized是Java的关键字,是一种同步锁。

synchronized的作用范围可以根据使用方式的不同而有所区别,主要有以下几种情况:

  • 同步方法(实例方法)
public class SynchronizedExample {
    public synchronized void syncMethod() {
        // 同步代码块
    }
}

synchronized修饰一个实例方法时,它作用于整个方法体。当一个线程进入一个对象的同步方法时,其他线程在该对象上调用同步方法时会被阻塞,直到第一个线程退出该方法。这种同步方式是基于对象的,也就是说,不同的对象实例的同步方法是互不干扰的。

  • 同步静态方法
public class SynchronizedExample {
    public static synchronized void syncStaticMethod() {
        // 同步代码块
    }
}

synchronized修饰一个静态方法时,它作用于整个静态方法体。由于静态方法是属于类的,而不是类的实例,因此这种同步是基于类的。当一个线程进入一个类的同步静态方法时,其他线程在该类上调用同步静态方法时会被阻塞。

  • 同步代码块
public class SynchronizedExample {
    private final Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // 同步代码块
        }
    }
}

synchronized也可以用来修饰一个代码块,此时需要指定一个对象作为锁对象。当线程进入同步代码块时,它会获取指定对象的锁,如果其他线程已经持有该对象的锁,则进入阻塞状态。这种同步方式允许更细粒度的控制,只同步需要同步的代码部分。 

2.1.2 多线程编程步骤

第一步:创建资源类,在资源类创建属性和操作方法。

第二步:创建多个线程,去调用资源类的操作方法。

2.1.3 Synchronized实现买票示例

需求:3个售票员卖一百张门票。

分析:资源是一百张门票,操作方法是买票,创建多个线程是3个售货员。

代码示例

// 第一步:定义资源类
class Ticket {
    // 第二步:定义资源
    public int number = 100;

    // 第三步:定义操作方法
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出第" + (100 - number + 1) + "张票,剩余" + --number + "张票");
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        // 使用匿名内部类创建线程
        Runnable runnable = () -> {
            for (int i = 0; i < 150; i++) {
                ticket.sale();
            }
        };

        // 创建多个线程进行卖票
        new Thread(runnable,"售票员1").start();
        new Thread(runnable,"售票员2").start();
        new Thread(runnable,"售票员3").start();
    }
}

输出结果

2.2 Lock

2.2.1 Lock接口的介绍

Lock 实现提供比使用 synchronized 方法和语句可以获得的更广泛的锁定操作。

2.2.2 使用Lock实现卖票例子

// 第一步:定义资源类
class Ticket {
    // 第二步:定义资源
    public int number = 100;

    // 创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    // 第三步:定义操作方法
    public synchronized void sale() {
        // 手动上锁
        lock.lock();
        try{
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出第" + (100 - number + 1) + "张票,剩余" + --number + "张票");
            }
        }finally {
            // 手动解锁
            lock.unlock();
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        // 使用匿名内部类创建线程
        Runnable runnable = () -> {
            for (int i = 0; i < 150; i++) {
                ticket.sale();
            }
        };

        // 创建多个线程进行卖票
        new Thread(runnable,"售票员1").start();
        new Thread(runnable,"售票员2").start();
        new Thread(runnable,"售票员3").start();
    }
}

2.2.3 synchronized和Lock两者差异

  • synchronized是java内置关键字。Lock不是内置,可以实现同步访问且比 synchronized中的方法更加丰富。
  • synchronized自动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)。
  • Lock 可以让等待锁的线程响应中断,而等待synchronized锁的线程不能响应中断,会一直等待。
  • 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
  • Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候,Lock 性能远远好于synchronized)。

2.2.4 创建线程的四种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable接口
  4. 使用线程池

3.线程间通信

3.1 多线程编程步骤

第一步:创建资源类,在资源类创建属性和操作方法。

第二步:在资源类操作方法,判断、干活、通知。

第三步:创建多个线程,去调用资源类的操作方法。

第四步:防止虚假唤醒。

3.2 synchronized 实现线程通信案例

关键字 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式。

代码示例

// 第一步:创建资源类,定义属性和操作方法
class Share {
    private int number = 0;

    // +1的方法
    public synchronized void incr() throws InterruptedException {
        // 第二步:判断
        if (number != 0) {
           this.wait();
        }
        // 干活
        number++;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        // 通知其他线程
        this.notifyAll();
    }

    // -1的方法
    public synchronized void decr() throws InterruptedException {
        // 判断
        if (number != 1) {
            this.wait();
        }
        // 干活
        number--;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        // 通知其他线程
        this.notifyAll();
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        // 第三步:创建多个线程,调用资源类中的操作方法
        Share share = new Share();

        new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "生产").start();

        new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "消费").start();

    }

}

3.3 虚假唤醒问题(if改while)

当多个线程都处于等待集合中,一旦收到通知,可以直接操作而不再判断,这叫做虚假唤醒问题。 将this.wait()放在while循环中可以解决该问题。

代码示例

class Share {
    int number = 0;

    public synchronized void incr() throws InterruptedException {
        //判断
        if (number != 0) {
            this.wait();//这里会释放锁
        }
        //执行
        number++;
        System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
        // 通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        //判断
        if (number != 1) {
            this.wait();
        }
        //执行
        number--;
        System.out.println(Thread.currentThread().getName() + " : " + number);
        //通知
        this.notifyAll();
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "D").start();
    }
}

输出结果

原因

由于 wait() 方法使线程在哪里睡就在哪里醒,B和D在wait后被唤醒,执行操作时不会再通过 if 判断,从而导致出现异常结果。
为了保证线程“醒了”之后再次判断,需要将wait() 方法放入while循环中。 

class Share {
    int number = 0;

    public synchronized void incr() throws InterruptedException {
        //判断
        while (number != 0) {
            this.wait();
        }
        //执行
        number++;
        System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
        // 通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        //判断
        while (number != 1) {
            this.wait();
        }
        //执行
        number--;
        System.out.println(Thread.currentThread().getName() + " : " + number);
        //通知
        this.notifyAll();
    }
}

3.4 Lock实现线程间通信案例

在 Lock 接口中,有一个 newCondition() 方法,返回一个新 Condition 绑定到该实例 Lock 实例。

Condition 类中有 await() signalAll() 等方法,和 synchronized 实现案例中的 wait() 和 notifyAll() 方法相同。

代码示例

class Share {
    private int number = 0;
    //创建Lock
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void incr() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                condition.await();
            }
            number++;
            System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void decr() throws InterruptedException {
        lock.lock();
        try {
            //判断
            while (number != 1) {
                condition.await();
            }
            //执行
            number--;
            System.out.println(Thread.currentThread().getName() + " : " + number);
            //通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "D").start();
    }
}

4.线程间定制化通信

4.1 Condition 类选择性通知

案例: 启动三个线程,按照如下要求执行,AA打印5此,BB打印10次,CC打印15次,一共进行10轮
具体思路: 每个线程添加一个标志位,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志位的线程。

代码示例

class ShareResource {
    // 标志位 1:AA 2:BB 3:CC
    private int flag = 1;
    private ReentrantLock lock = new ReentrantLock();
    // 创建三个Condition对象,实现定向唤醒
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1) {
                c1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 2;
            c2.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 3;
            c3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print15(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 1;
            c1.signal();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print10(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "BB").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print15(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "CC").start();
    }
}

上面的代码采用单标志法,设置一个公用整型变量flag,用于指示被允许进入临界区的进程编号。

若 flag =1,则允许 AA 进程进入临界区;

若 flag =2,则允许 BB 进程进入临界区;

若 flag =3,则允许 CC 进程进入临界区。

该算法可确保每次只允许一个进程进入临界区。但两个进程必须交替进入临界区,若某个进程不再进入临界区,则另一个进程也无法进入临界区。在线程的run()方法调用中设置不同的loop次数,在后期会有部分线程不能访问 Share 资源了,违背了"空闲让进"原则,让资源利用不充分。

4.2 进程/线程同步四个原则

  • 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
  • 忙则等待:当已经有进程进入临界区的时候,其他试图进入临界区的进程必须等待。
  • 有限等待:对请求访问的进程,应保证能在有限时间内进入临界区。
  • 让权等待:当进程不能进入临界区的时候,应立即释放处理机,防止进程忙等待。

5.集合的线程安全

集合线程不安全,简单来说就是底层的方法没有使用同步安全锁。

5.1 ArrayList 不安全

jdk源码

没有使用synchronized关键字,所以在多线程并发时,会出现线程安全问题。

代码示例

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

运行报错

5.2 解决方案 Vector

jdk源码

Vector类中的方法加了synchronized关键字,因此可以保证线程安全。

代码改造

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

 运行结果

5.3 解决方案 synchronizedList(List list) 

Collections 接口中的 synchronizedList(List list) 方法,可以将传入的 List列表对象转为同步(线程安全的)列表并返回。

语法

List<String> list = Collections.synchronizedList(new ArrayList<>());

jdk源码

代码示例

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

5.4 解决方案 CopyOnWriteArrayList

语法

List<String> list = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList 采用读写分离的思想,读操作不加锁,写操作加锁。(redis也使用)

  • 读的时候并发读取旧数据(多个线程操作)
  • 写的时候独立,先复制一份比旧数据长 1 的数据出来,在最后添加数据,旧新合并,完成写操作,之后就可以读所有数据(每次加新内容都写到新区域,合并之前旧区域,读取新区域添加的内容)

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

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

相关文章

自动驾驶仿真(高速道路)LaneKeeping

前言 A high-level decision agent trained by deep reinforcement learning (DRL) performs quantitative interpretation of behavioral planning performed in an autonomous driving (AD) highway simulation. The framework relies on the calculation of SHAP values an…

代码审计(1):CVE-2022-4957分析及复现

0x00漏洞描述&#xff1a; ѕрееdtеѕt iѕ а vеrу liɡhtԝеiɡ&#xff48;t nеtԝоrk ѕрееd tеѕtinɡ tооl imрlеmеntеd in Jаvаѕсriрt. Thеrе iѕ а Crоѕѕ-ѕitе Sсriрtinɡ vulnеrаbilitу in librеѕроndеd ѕрееdtеѕt…

大学侵权责任法试题及答案,分享几个实用搜题和学习工具 #其他#媒体#知识分享

当今社会&#xff0c;技术的发展给我们带来了许多便利&#xff0c;包括了许多实用的学习工具。 1.东西题库 这是一个网站 为学校教师提供试题试卷、课件及教案等服务的题库资源共享型网站&#xff0c;由必刷题、必刷卷教研团队研发与审核&#xff0c;涵盖初高中全学段、全学…

Foxmail邮箱的简单实用

Foxmail邮箱是我们办公邮箱中比较有代表性和使用性的一款邮箱软件&#xff0c;今天笔者为大家介绍一下Foxmail邮箱的功能和使用方法。 1、首先我们从安装Foxmail邮箱开始 2、点击安装等待安装成功 3、双击打开 &#xff0c;出现邮箱设置界面输入我们的账号密码&#xff0c;点击…

轻松解决问题!教你文件怎么解除只读模式!

在日常使用电脑时&#xff0c;我们有时会遇到文件或文件夹被设定为只读模式的情况&#xff0c;这可能会限制我们对文件的修改和编辑。然而&#xff0c;解除只读模式并获得文件的完全控制是一个相对简单的过程&#xff0c;只需要掌握一些基本的技巧和方法。在本文中&#xff0c;…

SpringBootWeb 篇-深入了解 AOP 面向切面编程与 AOP 记录操作日志案例

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 AOP 概述 1.1 构造简单 AOP 类 2.0 AOP 核心概念 2.1 AOP 执行流程 3.0 AOP 通知类型 4.0 AOP 通知顺序 4.1 默认按照切面类的类名字母排序 4.2 用 Order(数字) 注…

ubuntu-server(22.04)安装

准备工作 首先我们先从网上获取ubuntu的iso镜像文件 Index of /ubuntu-releases/22.04/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 我们安装这个最小包即可 找到我们ubuntu安装完成后所需要下载安装源的网址&#xff08;常用是阿里云&#xff09; ubuntu安装…

QT Creator与QT的下载安装

0.起因/小结&#xff1a; 因为运行项目需要更高版本的QT。 下载了QT 6.2.0&#xff0c;但是里面的gcc&#xff0c;g&#xff0c;gdb是64bit的&#xff0c;而我的QT Creator是32bit的&#xff0c;所以又下载了QT 13.0.0的64bit版本。 遇到问题&#xff1a;msvcp140_1.dll找不到…

TSINGSEE青犀视频:城市道路积水智能监管,智慧城市的守护者

随着城市化进程的加快&#xff0c;城市道路网络日益复杂&#xff0c;尤其在夏季&#xff0c;由于暴雨频发&#xff0c;道路积水问题成为影响城市交通和市民生活的重要因素之一。传统的道路积水监测方式往往依赖于人工巡逻和简单的监控设备&#xff0c;这些方法存在效率低下、响…

Qt5学习笔记(一):Qt Widgets Application项目初探

笔者长期使用MFC开发Windows GUI软件。随着软件向Linux平台迁移的趋势越发明朗&#xff0c;GUI程序的跨平台需求也越来越多。因此笔者计划重新抓一下Qt来实现跨平台GUI程序的实现。 0x01. 看看Qt Widgets Application项目结构 打开Qt5&#xff0c;点击“ New”按钮新建项目。…

C语言—内存函数

1. memcpy 使用和模拟实现 void* memcpy&#xff08;void* destination&#xff0c;const void* source&#xff0c;size_t num&#xff09;&#xff1b; 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。这个函数在遇到 ‘\0’ 的时候并不…

YOLOv8+PyQt5苹果叶病害检测(可以重新训练,yolov8模型,从图像、视频和摄像头三种路径识别检测)

效果视频&#xff1a;YOLOv8PyQt5苹果叶病害检测系统完整资源集合 资源包含可视化的苹果叶病害检测系统&#xff0c;基于最新的YOLOv8训练的苹果叶病害检测模型&#xff0c;和基于PyQt5制作的可视苹果叶病害系统&#xff0c;包含登陆页面和检测页面&#xff0c;该系统可自动检…

k8s AIOps

k8s AIOps 主要介绍下k8sgpt 官站 github 介绍 k8sgpt 是一个用于扫描Kubernetes集群、诊断和分级问题的工具。它以简单的英语呈现问题&#xff0c;并将站点可靠性工程&#xff08;SRE&#xff09;的经验编码到其分析器中。通过AI丰富问题的解释&#xff0c;k8sgpt帮助提取最…

简单聊下办公白环境

在当今信息化时代&#xff0c;办公环境对于工作效率和员工满意度有着至关重要的影响。而白名单作为一种网络安全策略&#xff0c;其是否适合办公环境&#xff0c;成为了许多企业和组织需要思考的问题。本文将从白名单的定义、特点及其在办公环境中的应用等方面&#xff0c;探讨…

企业数字化转型的主要方面有哪些?

本人研究企业数字化转型10余年&#xff0c;为企业软件选型、数字化提供咨询服务&#xff01;目前重点研究低代码数字化转型玩法&#xff0c;力争为各行各业探索出一条最具性价比的数字化方式。 关于“企业数字化转型包括哪些方面”这个问题&#xff0c;咱先来看个例子哈~ 比如…

常用的Linux命令,linux下文件的读、写、打开、关闭append用法

vim demoq.c打开写的.c文件 内容为 按a可以编辑页面代码。按ESC退出编辑然后按shift&#xff1a;wq保存文件并退出 Linux 系统中采用三位十进制数表示权限&#xff0c;如0755&#xff0c; 0644.7 124(可读、可写、可执行&#xff09; 5 14(可读、不可写、可执行&#xff09; …

西门子学习笔记9 - 中断式的触摸屏报警处理

Time delay interrupt"延时中断" OB&#xff08;达到设定延时时间后扫描一次程序&#xff0c;扫描完成后退出程序&#xff0c;继续执行主程序。&#xff09; 注&#xff1a;中断的触摸屏配置不需要改变&#xff0c;只是改变程序部分&#xff0c;在不需要使用的时候不…

WindowManager相关容器类

窗口中容器类介绍&#xff1a; 本节内容较多&#xff0c;建议结合前面的内容一起阅读&#xff1a; 1、addWindow的宏观概念 2、WindowManager#addView_1 3、WindowManager#addView_2 1&#xff09;、WindowContainer&#xff1a; class WindowContainer<E extends WindowC…

先激活还是先插卡?流量卡的激活顺序你知道吗?

拿到流量卡后&#xff0c;先激活还是先插卡吗&#xff1f;你知道是流量卡的激活顺序吗&#xff1f; 在这里&#xff0c;小编提醒大家&#xff0c;拿到卡后先别着急着操作&#xff0c;一定要先看一遍激活流程。 以下为流量卡的激活方法&#xff1a; 如果你是快递激活的话&…

ProxySQL + MySQL MGR 实现读写分离实战

文章目录 前言1、ProxySQL 介绍1.1、ProxySQL 如何工作1.2、ProxySQL 工作原理 2、ProxySQL 安装3、ProxySQL MGR 读写分离3.1、读写分离配置3.2、读写分离测试3.3、SpringBoot 整合 前言 该文章实践之前&#xff0c;需要搭建MySQL MGR集群&#xff0c;关于 MySQL MGR 集群搭…