JAVA多线程介绍

2023-11-19

1、什么是多线程

        得益于计算机的时间片机制,每一个应用程序的都可以在一段很小的时间段内执行。相比于单线程串行执行,得不到时间片就停止执行,多线程当中线程1得不到时间片,线程2有可能得到,可以更多的完成任务。

        还有一种场景,单线程要操作IO设备,但是IO设备一般都会有较多的等待,这段时间CPU处于空闲,空闲的CPU资源就被浪费了,如果能把资源让给另外一个需要CPU资源的线程,那就提高了任务完成的速率,让活干的更快。

        线程:拿微信举例,微信相当于一个进程,在微信启动后,向操作系统索要了一些资源的使用权限,线程相当于微信里面的打工人,这些打工人有很多岗位,比如收发消息岗、转账岗、抢红包岗等等,这些具体的工作,微信创建出来很多线程去干。怎么样让这些打打工人又快又好的干活呢,接着往下看。

2、JAVA多线程使用

2.1、创建线程

2.1.1、继承Thread类

        Thread类是JVM中用于管理线程的一个类,每一个线程都有唯一的一个Thread对象与之一一对应

package thread;

public class TestThread extends Thread {
    @Override
    public void run() {
        System.out.println("我是一个线程,我的名字是:"+Thread.currentThread().getName());
    }


    public static void main(String[] args) throws InterruptedException {
        TestThread testThread = new TestThread();
        testThread.start();
        testThread.join();
    }
}
2.1.1.1、Thread类及常见方法

         常见构造方法

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, String name) 使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group, Runnable target) 线程可以被用来分组管理,分好的组即使线程组

        常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()

 2.1.2、实现Runnable函数式接口

自JAVA诞生,Runnable就已经存在了,如果一个线程类要实现 Runnable 接口,则这个类必须定义一个名为 run 的无参数方法。

实现了 Runnable 接口的类可以通过实例化一个 Thread 实例,并将自身作为目标传递来运行。

Runnable定义: 

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Runnable使用举例: 

package thread;

public class TestThread implements Runnable {

    @Override
    public void run() {
        System.out.println("我是一个线程,我的名字是:" + Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {
        TestThread testThread = new TestThread();
        Thread thread = new Thread(testThread);
        thread.start();
        thread.join();

    }
}

函数式写法,这是JDK1.8之后,对Runnable加上了@FunctionalInterface函数式接口的修饰,可以实现函数式编程,也叫Lambda表达式,想了解Lambda表达式可以参考Lambda、函数式接口、Stream 一次性全给你

package thread;

public class TestThread  {

    public static void main(String[] args) {
        
        Runnable runnable = ()->{
            System.out.println("我是一个线程,我的名字是" + Thread.currentThread().getName());
        };

        Thread thread = new Thread(runnable);
        thread.start();
        
    }
}

注意,是调用新 new 出来的 Thread 实例的start() 方法,不要调用run方法,虽然我们是重写Runnable的 run方法的。调用 run方法并没有创建线程的效果,而是直接在当前线程执行,就和执行一个普通类的普通方法一模一样。

为什么要调用 start()方法呢,我们看看 Thread的 start()方法实现中,其实是调用了一个名称为 start0()的 native 方法,native 方法就不是用 Java 实现的了,而是在 JVM 层面的实现。

这个start0方法的主要逻辑就是启动一个操作系统线程,并和 JVM 线程绑定,开辟一些空间来存储线程状态和上下文的数据,然后执行绑定的 JVM 线程(也就是我们实现了Runnable的类)的 run方法的代码块,从而执行我们自定义的逻辑。

其实Runnable并不是很完美,其不能返回值,不能抛出异常,所以有了Callable,来实现这些功能。

2.1.3、实现Callable函数式接口

Callable定义

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Callable使用举例: 

package thread;

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

public class TestCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = () -> {
            int sum = 0;
            for (int i = 0; i < 5; ++i) {
                sum += i;
                //TimeUnit.SECONDS.sleep(1);
                throw new Exception("业务出现异常");
            }
            return sum;
        };

        FutureTask<Integer> integerFutureTask = new FutureTask<>(callable);
        Thread thread = new Thread(integerFutureTask);
        thread.start();
        System.out.println("我是main线程");
        Integer result = null;
        try {
            result = integerFutureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }catch (Exception e) {
            System.out.println("处理异常:"+e.getMessage());
            e.printStackTrace();
        }
        System.out.println("最终运行结果为:"+result);
    }
}

 2.1.4、Thread、Runnable、Callable之间的区别

        本质上,Runnable和Callable只是Thread要执行的任务类,并且Runnable和Callable是函数式接口。在java中一个类只能继承一个父类,可以实现多个接口,Runnable和Callable方式的好处是可以规避类的单继承的限制;

        Runnable和Callable之间的区别:

                Callable实现的是call()方法,Runnable实现的是run()方法

                Callable可以通过FutureTask对象获取返回值,而Runnable则没有返回值

                Callable可以抛出异常,而Runnable不支持

                Callable要搭配FutureTask一起使用

2.2、线程控制

2.2.1、中断一个线程

常见的有以下两种方式:

        1、通过共享的标记来沟通

        2、调用interrupe()方法来通知

示例1:

package thread;

public class ThreadDemo {
    private static class MyRunnable implements Runnable {
        public volatile boolean isQuit = false;
        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        target.isQuit = true;
    }
}

示例2:

package thread;


public class ThreadDemo {

    private static class MyRunnable implements Runnable {

        @Override
        public void run() {
// 两种方法均可以
//            while (!Thread.interrupted()) {
while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内鬼,终止交易!");
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()
                    + ": 啊!险些误了大事");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName()
                + ": 让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
    }

}

 重点说明下第二种方法:

        1、通过 thread 对象调用 interrupt() 方法通知该线程停止运行

        2、thread 收到通知的方式有两种:

                1、如果线程调用了wait()/join()/sleep()方法而阻塞挂起,则以InterruptedException异常的方式通知,清楚中断标志。

                2、否则,只是内部的中断标识被设置,需要手动判断,在判断上也有区别;thread.interrupted()判断当前线程的中断标志,清除中断标志;thread.currentThread().isInterruptd(),判断指定线程的中断标识是否被限制,不清除中断标志。

方法 说明
public void interrupt()  中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位
public static boolean interrupted() 判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean isInterrupted() 判断对象关联的线程的标志位是否设置,调用后不清除标志位

2.2.2、等待一个线程join

package thread;

import java.util.concurrent.atomic.AtomicInteger;

public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        //原子整型
        AtomicInteger atomicInteger = new AtomicInteger();
        Thread thread = new Thread(() -> {
            //五万次自增操作
            for (int i = 0; i < 50000; i++) {
                atomicInteger.getAndIncrement();

            }
        });
        Thread thread2 = new Thread(() -> {
            //五万次自增操作
            for (int i = 0; i < 50000; i++) {
                atomicInteger.getAndIncrement();

            }
        });
        //启动线程
        thread.start();
        thread2.start();
        //等待两个线程执行完成
        thread.join();
        thread2.join();
        System.out.println(atomicInteger);
    }

}

想要得到线程的执行结果,就要等待线程执行完毕,join方法可以等待线程执行完毕,这里值得说明的是:Java 中的多线程代码不会随着主线程的退出而退出,在JAVA中,所有的线程都是通过线程对象来创建和管理的,点一个JAVA应用程序启动时,至少有一个主线程在运行,当主线程启动并创建其他线程时,这些线程就成了独立的执行流,可以在主线程退出之后,继续执行,每个线程都有自己的生命周期,他们独立于主线程的运行状态。

        与 Java 不同,C++ 中的多线程代码会随着主线程的退出而退出,这是因为 C++ 标准库对线程的处理方式与 Java 不同。C++ 的线程是基于操作系统的线程模型实现的,而不是像 Java 那样由虚拟机管理。当主线程退出时,C++ 运行时库会通知操作系统关闭所有线程。主线程退出意味着整个进程退出,所有线程都会被终止。

需要注意的是,无论是在 Java 还是在 C++ 中,如果某个线程持有资源(如文件句柄、数据库连接等),而其他线程仍在使用这些资源,那么这些资源并不会自动释放。这需要开发者在编写代码时正确管理资源,并确保适当地关闭或释放资源。

方法 说明
public void join() 等待线程结束
public void join(long millis) 等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos) 同理,但可以更高精度

2.2.3、休眠当前线程

2.2.3.1、sleep
方法 说明
public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠
    public class ThreadDemo {
        public static void main(String[] args) throws InterruptedException {
            System.out.println(System.currentTimeMillis());
            Thread.sleep(3 * 1000);
            System.out.println(System.currentTimeMillis());
        }
    }
2.2.3.2、wait 

wait方法使当前线程停止运行。

1、wait()方法是Object类的方法,该方法是将当前线程放入到线程就绪队列中,在wait()方法之后停止运行,直到接到通知或者被中断为止。

2、wait()方法只能在同步方法中或同步代码块中调用,如果调用是没有适当的锁,或抛出异常。

3、wait()方法调用后,当前线程释放锁,知道其他线程调用此线程对象的notify()方法或是notifyAll()方法,当前线程被唤醒进入就绪状态。

4、wait((long timeout))让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的notify()方法或 notifyAll() 方法, 或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。

2.2.3.3、notify方法

使被wait的线程继续运行。

1、方法notify()也要在同步方法或者是同步代码块中被调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑选出一个呈wait状态的线程。

2、在执行完notify()方法之后,当前线程不会马上释放锁,而是要等待当前线程执行完notify()之后的代码,也就是退出同步代码块之后再释放锁。

package thread;

public class TestNotify implements Runnable {
    private boolean flag;
    private Object obj;
    public TestNotify(boolean flag, Object obj) {
        super();
        this.flag = flag;
        this.obj = obj;
    }
    public void waitMethod() {
        synchronized (obj) {
            try {
                while (true) {
                    System.out.println("wait()方法开始.. " +
                            Thread.currentThread().getName());
                    obj.wait();
                    System.out.println("wait()方法结束.. " +
                            Thread.currentThread().getName());
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public void notifyMethod() {
        synchronized (obj) {
            try {
                System.out.println("notify()方法开始.. " + Thread.currentThread().getName());
                obj.notify();
                System.out.println("notify()方法结束.. " + Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void run() {
        if (flag) {
            this.waitMethod();
        } else {
            this.notifyMethod();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        TestNotify waitThread = new TestNotify(true, object);
        TestNotify notifyThread = new TestNotify(false, object);
        Thread thread1 = new Thread(waitThread, "wait线程");
        Thread thread2 = new Thread(notifyThread, "notify线程");
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
        System.out.println("main方法结束!!");
    }
}
2.2.3.4、notifyAll方法

 此方法会唤醒所有的在等待中的线程,使其正常执行

package thread;
class TestNotifyAll implements Runnable {
    private boolean flag;
    private Object obj;
    public TestNotifyAll(boolean flag, Object obj) {
        super();
        this.flag = flag;
        this.obj = obj;
    }
    public void waitMethod() {
        synchronized (obj) {
            try {
                while (true) {
                    System.out.println("wait()方法开始.. " +
                            Thread.currentThread().getName());
                    obj.wait();
                    System.out.println("wait()方法结束.. " +
                            Thread.currentThread().getName());
                    return;
                }
            } catch (Exception e) {e.printStackTrace();
            }
        }
    }
    public void notifyMethod() {
        synchronized (obj) {
            try {
                System.out.println("notifyAll()方法开始.. " +
                        Thread.currentThread().getName());
                obj.notifyAll();
                System.out.println("notifyAll()方法结束.. " +
                        Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void run() {
        if (flag) {
            this.waitMethod();
        } else {
            this.notifyMethod();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        TestNotifyAll waitThread1 = new TestNotifyAll(true, object);
        TestNotifyAll waitThread2 = new TestNotifyAll(true, object);
        TestNotifyAll waitThread3 = new TestNotifyAll(true, object);
        TestNotifyAll notifyThread = new TestNotifyAll(false, object);
        Thread thread1 = new Thread(waitThread1, "wait线程A");
        Thread thread2 = new Thread(waitThread2, "wait线程B");
        Thread thread3 = new Thread(waitThread3, "wait线程C");
        Thread thread4 = new Thread(notifyThread, "notify线程");
        thread1.start();
        thread2.start();
        thread3.start();
        Thread.sleep(1000);
        thread4.start();
        System.out.println("main方法结束!!");
    }
}

2.3、线程状态

线程一共有6种状态。

 3、线程间同步

线程间同步的含义是线程间对临界资源操作的合理性;那生产者和消费者模型举个例子,生产者生产三个资源,消费者就能消费三个资源,当资源不足时消费者要通知生产者生产资源消费者等待,生产完资源后生产者要通知消费者消费,生产者等待。

3.1、Semaphore信号量

信号量:表示可用资源的数量,本质上就是一个计数器,比如停车场有100个车位,那么就只有100个资源,停进去一辆资源-1,出来一辆资源+1,当资源为0的时候,想进停车场就得等待

方法 说明

semaphore.acquire()

对资源-1

semaphore.release()

对资源+1
package thread;

import java.util.concurrent.Semaphore;

public class TestSemaphore {
    //信号量

    public static Semaphore semaphore = new Semaphore(3);

    public void testSemaphoreFunc()
    {
        System.out.println("我的名字是:"+Thread.currentThread().getName());
        try {
            semaphore.acquire();
            System.out.println("成功获取到一个资源,我的名字是:"+ Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("资源使用完毕,我的名字是:"+ Thread.currentThread().getName());
        semaphore.release();
    }

    public static void main(String[] args) throws InterruptedException {

        TestSemaphore testSemaphore = new TestSemaphore();

        for (int i = 0; i < 10; ++i) {
            Thread thread = new Thread(() -> testSemaphore.testSemaphoreFunc());

            thread.start();
            thread.join();
        }

        //定义一个任务
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("我的ID是:"+Thread.currentThread().getName());
                try {
                    semaphore.acquire();
                    System.out.println("成功获取到一个资源,我的ID是:"+ Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("资源使用完毕,我的ID是:"+ Thread.currentThread().getName());
                semaphore.release();
            }
        };

        for (int i = 0; i < 10; ++i) {
            Thread thread = new Thread(runnable);
            thread.start();
            thread.join();

        }


    }
}

3.2、volatile关键字

修饰的共享变量,可以保证可见性。

可见性的含义:为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线程之间不能及时看到改变。

将共享变量用volatile修饰,线程对变量做了改变就能及时刷新进主内存中,保证变量可见性。 

3.3、条件变量

java中没有与C++中条件变量一样的工具,但是 Java 提供了等待/通知机制来实现类似的功能。这个机制是通过线程对象的 wait()notify()notifyAll() 方法来实现的。

4、线程间互斥

参考另一篇文章,待续。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA多线程介绍 的相关文章

  • 为什么会出现此异常 FileItemStream$ItemSkippedException?

    在 gwt Web 应用程序中 我必须发送一个文件和附加的一些参数 在服务器端 try ServletFileUpload upload new ServletFileUpload FileItemIterator iterator upl
  • 具有默认值的 Java JAX-RS 自定义参数

    假设我有这个 这只是一个示例 GET Path value address Produces application json public Response getAddress QueryParam user User user 用户是
  • 位图内存不足错误

    我对这个错误有疑问 我从 URL 制作网站图标解析器 我这样做是这样的 public class GrabIconsFromWebPage public static String replaceUrl String url StringB
  • JavaFX 图像未在舞台中显示

    我尝试了很多次 尝试了很多方法 但都无法让自己的形象在舞台上如我所愿 我认为这可能与java寻找资源的路径有关 但我不确定 因为我刚刚开始使用视觉库 在本例中为JavaFX 这是我的目录结构 MyProject assets img myI
  • 在Java中使用命令行编译多个包

    您好 我一直在使用 IDE 但现在我需要从命令行运行和编译 问题是我有多个软件包 我试图找到答案 但没有任何效果 所以我有 src Support java files Me java files Wrapers java files 你知
  • Jackson - 反序列化嵌套 JSON

    我有一个 JSON 字符串 其格式如下 response execution status ready report cache hit true created on 2013 07 29 08 42 42 fact cache erro
  • 解决 Java Checkstyle 错误:名称 'logger' 必须匹配模式 '^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$'

    使用 Eclipse Checkstyle 插件我看到以下错误 名称 logger 必须匹配模式 A Z A Z0 9 A Z0 9 我通过更改解决了此错误 private static final Logger logger Logger
  • Java、Oracle 中索引处缺少 IN 或 OUT 参数:: 1 错误

    您好 我使用 Netbeans 8 0 2 和 Oracle 11g Express Edition 在 JSF 2 2 中编写了一个图书馆管理系统 我有几个名为 书籍 借阅者 等的页面 以及数据库中一些名为相同名称的表 我的问题是这样的
  • 将过滤器添加到 Eclipse 中的 Project Explorer

    我想向 Project Explorer 添加一个新的过滤器 以向用户隐藏一些在 Eclipse RCP 应用程序中自动创建的项目 到目前为止我已经找到了两个扩展点 org eclipse ui ide resourceFilters 允许
  • java.lang.LinkageError:尝试重复的类定义

    为什么会发生错误以及如何修复它 02 13 02 pool 4 thread 2 WARN Exception in thread pool 4 thread 2 02 13 02 pool 4 thread 2 WARN java lan
  • Java Microsoft Excel API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • JFace ColumnWeigthData 导致父级增长

    我有一个 Eclipse RCP 应用程序 并且想要在TableViewer using ColumnWeigthData as ColumnLayoutData 问题是父表单 ScrolledForm在示例代码中 每当我布局表格时都会增加
  • Java-如何将黑白图像加载到二进制中?

    我在 FSE 模式下使用 Java 和 swing 我想将完全黑白图像加载为二进制格式 最好是二维数组 并将其用于基于掩码的每像素碰撞检测 我什至不知道从哪里开始 过去一个小时我一直在研究 但没有找到任何相关的东西 只需将其读入Buffer
  • 从 @JsonProperty 值获取枚举常量

    我有一个标有 JsonProperty 的枚举 用于使用 Jackson 进行 JSON 序列化 反序列化 并且希望获取给定字符串 JsonProperty 的枚举值 public enum TimeBucket JsonProperty
  • 如何将 Observable>> 转换为 Observable>

    我陷入了如何将以下可观察类型转换 转换为我的目标类型的困境 我有以下类型的可观察值 Observable
  • 创建正则表达式匹配数组

    在Java中 我试图将所有正则表达式匹配返回到一个数组 但似乎您只能检查模式是否匹配某些内容 布尔值 如何使用正则表达式匹配来形成与给定字符串中的正则表达式匹配的所有字符串的数组 4城堡的回答 https stackoverflow com
  • 改变for循环的顺序?

    我遇到一种情况 我需要根据用户输入以不同的顺序循环遍历 xyz 坐标 所以我是 3D 空间中的一个区域 然后是一组像这样的 for 循环 for int x 0 x lt build getWidth x for int y 0 y lt
  • Java中的回调接口是什么?

    SetObserver 接口的代码片段取自有效的Java 避免过度同步第67条 public interface SetObserver
  • 为什么这个私人浮动字段变为零?

    我有一些奇怪的行为 我很难向自己解释 称为 textureScale 的浮点字段变为零 如果某些代码正在更改该值 则可以解释这一点 然而 我希望能够通过将其设置为 私有最终浮点 来导致构建失败 或者至少是运行时异常 那么无论更改该值都将失败
  • 如何用表达式语言获取布尔属性?

    如果我有一堂这样的课 class Person private int age public int getAge return age public boolean isAdult return age gt 19 我可以得到age像这样

随机推荐

  • Minikube 架构及启动流程剖析

    原文作者 wzqnls 编辑 夏天 对于要学习 Kubernetes 或者需要本地开发的开发人员来说 Minikube 是一个不错的选择 通过使用 Minikube 这个工具 我们可以非常快捷地在本地部署一套单节点的 Kubernetes
  • 【Linux】线程安全

    文章目录 1 线程互斥 1 1 线程间互斥的相关概念 1 2互斥量 1 3互斥量接口 1 4互斥量实现原理 2 可重入VS线程安全 3 常见锁概念 3 1死锁 3 2常见死锁情况 3 2 1情况一 忘记释放锁 3 2 2情况二 线程重复申请
  • [机缘参悟-66]:怎样才能让别人愿意帮你:利益共享法则、“大道”、“人性”

    目录 前言 第1章 生命是利益 1 1 什么是利益 1 2 不同时期 利益展现不同的形态 1 3 利益是维系社会运行的根本力量 1 4 利益是中性词 第2章 共享利益 2 1 共享利益的形态 2 2 显性的共享利益 物质利益 2 3 利益的
  • 分享一个嘉立创封装库(内含AD和PADS两种格式)

    一直以来做封装都是令我头疼的问题 偶然发现嘉立创的封装库 真的非常好用 而且封装做得非常漂亮 这个封装做得非常好 我也打过几款板子出来 手工焊接起来也非常好 真的是非常好的一个封装库 封装库里面包含了AD Protel99和PADS三种格式
  • 给windows宿主机和wsl2的ubuntu-20.04分配固定IP,使能相互ping通

    我们知道wsl2是基于hyper v的虚拟机 每次重新启动的时候 都会重新拉一个新的hyper v虚拟机实例 然后虚拟网卡的IP是dhcp随机分配的 如果作为开发系统用 就会比较烦每次都要换一个IP 有人提供了个脚本 他写了个bat脚本在w
  • Tomcat之startup.bat启动闪退解决

    安装完了service 那个服务器 使用从官网下载的apche包 我使用的是这个包apache tomcat 8 5 81 windows x64 去bin里面启动 startup bat结果出现闪退 问题还是java环境变量的设置问题 可
  • 用户的计算机名,获取计算机名及用户名

    ifdef WINDOWS uses Windows endif ifdef UNIX uses BaseUnix endif ifdef UNIX function GetUserName String begin Result GetE
  • docker介绍

    公式 Usage docker OPTIONS COMMAND A self sufficient runtime for containers Options config string Location of client config
  • Ipv4学习笔记之实践篇

    什么是IP 学习IP是入门网络的第一步 要想了解网络的工作原理 首先要了解的就是IP协议 IP standards for Internet Protocol 也就是说IP是Internet Protocol的缩写 是internet通信协
  • 【Termux Python3.11开发】安装opencv-contrib-python后终于可以尝鲜airtest,poco

    无意看到airtest的一些介绍 正好在找一些工具 Python自动化的轮子 好放在Termux环境下进行测试效果如何 经过一些时间的折腾 总算顺利解决 安装好几个相关的库 点击链接加入群聊 Termux友情赞助群 897177804 pi
  • uniapp小程序跳转其他小程序uni.navigateToMiniProgram效果demo(整理)

    放点击事件里面即可 uni navigateToMiniProgram appId 跳转的小程序的aooId path pages index index id 123 如果这里不填 默认是跳转到对方小程序的主页面 extraData 需要
  • 无人机+三维实景建模助力古建筑保护,传承历史记忆

    历史文化建筑 承载着过去各个时代的文化记忆 无论是保存还是修缮古建筑 都需要将其基本信息进行数字化建档 为修缮提供精准参考 根据住建部的要求 从2020年开始到2022年 全国需完成历史建筑100 测绘及系统录入工作 并且明确鼓励采用摄影测
  • iOS-根据系统语言更改App名称或其他配置

    要求 要根据系统的语言更改app的名字 解决方案 在xcode中进行打包前的配置 我用的是xcode11版本 一 Bundle display name 可以通过直接修改Bundle display name来确定app的名称 Bundle
  • 用云服务器搭建虚拟主机,如何用云服务器搭建虚拟主机

    如何用云服务器搭建虚拟主机 内容精选 换一换 在云服务器上搭建网站后 部分客户通过本地网络访问网站时出现偶发性无法访问的情况 确认客户使用的本地网络 若客户的本地网络是NAT网络 本地主机通过NAT功能使用公网IP地址访问弹性云服务器 可能
  • Windows 11开启硬件加速后出现的黑屏、闪屏(如Edge浏览器、照片)问题的两种解决方案

    2022年3月21日更新 若只有Edge出现闪屏问题 可跳到下方查看原文章 若其他软件也出现闪屏问题的话 可能是Intel核显驱动的问题 可以到Intel官网搜索相应的驱动程序 不要下载最新版 core 6 11代驱动下载地址 https
  • Verilog之assign

    Verilog中的关键词assign主要用于如下两个地方 数据流建模 用于数据流建模的显示连续赋值语句语法格式如下
  • 【数学建模】随机森林预测(Python代码实现)

    目录 1 参数 2 算例实现 2 1 算例 2 2 单目标预测 DecisionTreeRegressor 2 3 多目标预测MultiOutputRegressor 1 参数 n estimators 森林中决策树的数量 默认100 表示
  • Oracle 查询技巧与优化(二) 多表查询

    前言 上一篇blog介绍了Oracle中的单表查询和排序的相关技巧 http blog csdn net wlwlwlwl015 article details 52083588 本篇blog继续介绍查询中用的最多的 多表查询的技巧与优化方
  • VM装MACos

    准备工具 下载macOS Ventura 13 ISO镜像文件 VMware Workstation Pro最新版并激活 自行官网下载即可 需要镜像和key可以最下边的云盘自取 下载Unlocker for VMware Workstati
  • JAVA多线程介绍

    1 什么是多线程 得益于计算机的时间片机制 每一个应用程序的都可以在一段很小的时间段内执行 相比于单线程串行执行 得不到时间片就停止执行 多线程当中线程1得不到时间片 线程2有可能得到 可以更多的完成任务 还有一种场景 单线程要操作IO设备