Java——多线程使用详解

2023-11-13

多线程:
  • 多线程就是同时执行多个应用程序,需要硬件的支持
  • 同时执行:不是某个时间段同时,cpu切换的比较快,所有用户会感觉是在同时运行
并发与并行:
  • 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。并行必须借助于多核cpu实现

  • 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,通过cpu时间片轮转使多个进程快速交替的执行。

程序、线程、进程:

程序:是指编译好的可执行文件,程序启动的时候,进程作为支撑
进程:是正在运行的程序(比如360杀毒软件),进程可以产生多线程

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
进程基本的状态有5种:分别为初始态、就绪态、运行态、挂起态、终止态其中初始态为进程准备阶段,常与就绪态结合来看。

线程:是程序正在做的事情,线程是进程的单个控制流(比如360的杀毒,扫描木马)

单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序

进程与线程的区别:

  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

多线程同时执行原理:

比如我们同时运行qq和微信,其实不是同时运行的而是CPU在多个线程间快速切换,造成"同时"执行的假象

多线程的好处:

  • 可以"同时"执行多个任务
  • 可以提高资源的利用率(CPU/网络)

线程越多越好吗:

1.创建和销毁线程需要消耗CPU和内存资源
2.线程太多,CPU需要在大量的线程间切换,造成资源的浪费

进程与并发:
在使用进程实现并发时会出现以下问题:

  1. 系统开销比较大,占用资源比较多,开启进程数量比较少。
  2. 在unix/linux系统下,还会产生孤儿进程僵尸进程。正常情况下,子进程是通过父进程fork创建的,子进程再创建新的进程。并且父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用系统调用取得子进程的终止状态。

孤儿进程:

父进程比子进程先结束,子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

僵尸进程:

僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

Windows&Linux进程:

Windows下的进程和Linux下的进程是不一样的,它比较懒惰,从来不执行任何东西,只是为线程提供执行环境。然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候,操作系统会自动创建这个进程的第一个线程,成为主线程。

创建线程:

实现多线程有三种方法:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

开启线程

方法名 说明
void run() 在线程开启后,此方法将被调用执行
void start() 使此线程开始执行,Java虚拟机会调用run方法()

为什么要重写run()方法?

因为run()是用来封装被线程执行的代码

run()方法和start()方法的区别?

  • run():封装线程执行的代码,直接调用,相当于普通方法的调用,并没有开启线程
  • start():启动线程;然后由JVM调用此线程的run()方法
继承Thread:

实现步骤:

  • 定义一个类MyThread继承Thread类
  • 在MyThread类中重写run()方法
  • 创建MyThread类的对象
  • 启动线程

多线程类:

public class ThreadDemo extends Thread {
    // run是用来封装被线程执行的代码的,一定要重写
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程启动" + i);
        }
    }
}

测试类:

public class TestThread {
    public static void main(String[] args) {
        // 创建线程对象
        ThreadDemo thread1 = new ThreadDemo();
        ThreadDemo thread2 = new ThreadDemo();
        // 开启线程
        thread1.start();
        thread2.start();
    }
}
实现Runnable:

实现步骤:

  • 定义一个类MyRunnable实现Runnable接口
  • 在MyRunnable类中重写run()方法
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
  • 启动线程

对象类:

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程启动" + i);
        }
    }
}

测试类:

public class TestRunnable {
    public static void main(String[] args) {
        // 创建参数对象
        RunnableDemo r1 = new RunnableDemo();
        RunnableDemo r2 = new RunnableDemo();
        // 创建线程
        Thread thread1 = new Thread(r1);
        Thread thread2 = new Thread(r2);
        // 开启线程
        thread1.start();
        thread2.start();
    }
}
实现Callable接口:

实现步骤:

  • 定义一个类MyCallable实现Callable接口
  • 在MyCallable类中重写call()方法
  • 创建MyCallable类的对象
  • 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
  • 创建Thread类的对象,把FutureTask对象作为构造方法的参数
  • 启动线程
  • 再调用get方法,就可以获取线程结束之后的结果。

构造方法:

方法名 说明
Thread(Runnable target) 分配一个新的Thread对象
Thread(Runnable target, String name) 分配一个新的Thread对象

对象类:

public class CallDemo implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程执行" + i);
        }
        // 返回值表示运行完以后的结果
        return "执行完毕";
    }
}

测试类:

public class TestCall {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建对象,线程要执行的代码
        CallDemo call = new CallDemo();
        // 获取线程执行完毕的结果,可以作为参数传给Thread
        FutureTask<String> sft = new FutureTask<>(call);
        Thread thread = new Thread(sft);
        thread.start();
        // get:获取线程运行后的结果,如果在线程没开启前就获取get方法会一直等待,所以get方法要写在start后面
        System.out.println(sft.get());  
    }
}
三种实现方式对比:

实现Runnable、Callable接口

好处: 扩展性强,实现该接口的同时还可以继承其他的类
缺点: 编程相对复杂,不能直接使用Thread类中的方法

继承Thread类

好处: 编程比较简单,可以直接使用Thread类中的方法
缺点: 可以扩展性较差,不能再继承其他的类

获取与设置线程名称:
方法名 说明
void setName(String name) 将此线程的名称更改为等于参数name
String getName() 返回此线程的名称
Thread currentThread() 返回对当前正在执行的线程对象的引用

对象类:

public class ThreadDemo extends Thread {
 // 要写构造,否则不能传线程名称
    public ThreadDemo() {
    }

    public ThreadDemo(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + i);
            // 获取当前线程对象
        }
        // 线程没有设置名字的话,返回的是默认的名字,每个线程都是有默认名字的
            System.out.println("当前执行的线程是:" + Thread.currentThread().getName());
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        t1.start();
        t1.setName("线程一");

        // 通过构造设置,对象要创建有参构造
        ThreadDemo t2 = new ThreadDemo("线程二:");
        t2.start();

        // 如果是通过接口创建线程那么是不能用getname的,所以currentThread就可以代替了
        System.out.println(Thread.currentThread().getName());
    }
}
线程睡眠:
方法名 说明
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数

对象类:

public class DemoRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        DemoRunnable dr = new DemoRunnable();
        Thread thread = new Thread(dr);
        thread.start();
    }
}
线程优先级:
  1. 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU的时间
  2. 抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
  • Java使用的是抢占式调度模型,线程优先级高只是枪战CPU的几率更大,不代表一定优先执行
方法名 说明
final int getPriority() 返回此线程的优先级
final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
public class Test {
    public static void main(String[] args) {
        DemoRunnable dr = new DemoRunnable();
        Thread thread = new Thread(dr);
        thread.start();

        // final int getPriority()	返回此线程的优先级
        System.out.println(thread.getPriority());

        // final void setPriority(int newPriority)	更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
        thread.setPriority(10);
        System.out.println(thread.getPriority());
    }
}
守护线程:
方法名 说明
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

线程一:

public class Thread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + " :" + i);
        }
    }
}

线程二:

public class Thread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " :" + i);
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        // 创建线程
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();

        // 设置线程名字
        t1.setName("我女朋友");
        t2.setName("我");

        // 设置我女朋友为守护线程,我要挂了我女朋友也不能继续活,这就是家庭地位
        // 因为此时守护线程还有执行权,cpu执行很快,所以守护线程不是马上停止的,要把执行权走完
        t1.setDaemon(true);
        t1.start();
        t2.start();
    }
}
线程安全:

卖票案例分析线程安全:

重复票:定义三个线程去卖100张票,三个线程sleep后,第一个线程醒来拿到CPU执行权,此时将票减1,为99,刚减完线程二醒了。线程二拿到CPU执行权,此时将票减1,这时候是从99减1,为98,那这时候线程一也要变成98,因为执行的是同一个线程对象。刚减完线程三醒了。线程二拿到CPU执行权,此时将票减1,这时候是从98减1,为97…这时候就会出现重复票数
负数票:此时票数为1,线程1、2、3开始sleep,线程1醒来后,拿到CPU执行权,做减1操作,此时票数为0,线程1被销毁。线程2醒过来,然后拿到CPU执行权,做减1操作,此时票数为-1,线程2被销毁。线程3醒过来拿到CPU执行权,做减1操作,此时票数为-2,线程3被销毁。所以就会出现负数票问题

安全问题出现的条件

  1. 是多线程环境
  2. 有共享数据
  3. 有多条语句操作共享数据

如何解决多线程安全问题呢?

把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行,Java提供了同步代码块的方式来解决,
例:两个人同时上厕所,但是就一个马桶,于是用户A进入后,用户B只能在外面等,只有用户A出来,用户B才能进去。

同步代码块:
    synchronized(任意对象){  多条语句操作共享数据的代码  }
    // synchronized(任意对象):默认情况是打开的,只要有一个线程进去执行代码,锁就会关闭,当线程执行完出来,锁才会自动打开

同步代码块的好处和弊端:

好处:解决了多线程的数据安全问题
弊端:当线程很多时,每个线程都会去判断同步上的锁,很耗费资源,会降低程序的运行效率

对象类:

public class SellTicket implements Runnable {
    // 定义总票数
    private static int tickets = 100;
    // 创建一个任意的对象
    private final Object object = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (tickets > 0) {

                    try {
                        // 进来先睡一会
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--;
                } else {
                    break;
                }
            }
        }
    }
}

测试类:

public class SellTicketDemo {
    public static void main(String[] args) {
        // 创建对象
        SellTicket st = new SellTicket();
        // 创建线程并设置名字,多线程同一个对象
        Thread t1 = new Thread(st, "窗口一");
        Thread t2 = new Thread(st, "窗口二");
        // 启动线程
        t1.start();
        t2.start();
    }
}
同步方法:

同步方法:锁对象是:this

修饰符  synchronized 返回值类型 方法名(方法参数) {方法体;}

对象类:

public class SellTicket2 implements Runnable {
    // 定义总票数
    private static int tickets = 100;
    // 创建一个任意的对象
    private final Object object = new Object();

    @Override
    public void run() {

        while (true) {
            // 同步方法
            // 判断当前线程,是就调用方法,然后判断票数是不是0,不是就继续循环
            if ("窗口一".equals(Thread.currentThread().getName())) {
                boolean result = synchronizedMethod();
                if (result) {
                    break;
                }
            }
            // 同步代码块
            if ("窗口二".equals(Thread.currentThread().getName())) {
                // 因为同步方法的锁是this,所以代码块也要是this才能都用一把锁
                synchronized (this) {
                    if (tickets > 0) {
                        try {
                            // 进来先睡一会
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                        tickets--;
                    } else {
                        break;
                    }
                }
            }
        }
    }

    private synchronized boolean synchronizedMethod() {
        if (tickets > 0) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
            tickets--;
            // 不是最后一张就继续循环
            return false;
        } else {
            // 最后一张就返回true,停止
            return true;
        }
    }
}

测试类:

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket2 st = new SellTicket2();
        Thread t1 = new Thread(st, "窗口一");
        Thread t2 = new Thread(st, "窗口二");
        t1.start();
        t2.start();
    }
}

静态同步方法:锁对象是:类名.class

修饰符 static synchronized 返回值类型 方法名(方法参数) {方法体;}

静态方法在方法前加static,this换成类名.class就行了

Lock锁:

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,可以用它的实现类ReentrantLock来实例化

ReentrantLock构造方法

方法名 说明
ReentrantLock() 创建一个ReentrantLock的实例

加锁解锁方法

方法名 说明
void lock() 获得锁
void unlock() 释放锁

对象类:

public class Ticket implements Runnable {

    // 票的数量
    private int ticket = 100;

    // 创建Lock锁
    private ReentrantLock lock = new ReentrantLock();

    @Override

    public void run() {
        while (true) {
            try {
                // 获得锁
                lock.lock();
                if (ticket == 0) {
                    // 卖完了
                    break;
                } else {
                    Thread.sleep(30);
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

测试类:

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket, "线程一");
        Thread t2 = new Thread(ticket, "线程二");

        // 设置优先级
        t1.setPriority(10);
        t2.setPriority(5);

        t1.start();
        t2.start();
    }
死锁:

线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

死锁产生的条件:

1.有多个线程
2.有多把锁
3.有同步代码块嵌套

解决办法:

干掉其中某个条件

public class Demo05 {
	public static void main(String[] args) {
	MyRunnable mr = new MyRunnable();
	new Thread(mr).start();
	new Thread(mr).start();
	}
}


class MyRunnable implements Runnable {
    Object objA = new Object();
    Object objB = new Object();

    /*
    嵌套1 objA
    嵌套1 objB
    嵌套2 objB
    嵌套1 objA
    */
    @Override
    public void run() {
        synchronized (objA) {
            System.out.println("嵌套1 objA");
            synchronized (objB) {// t2, objA, 拿不到B锁,等待
                System.out.println("嵌套1 objB");
            }
        }
        synchronized (objB) {
            System.out.println("嵌套2 objB");
            synchronized (objA) {// t1 , objB, 拿不到A锁,等待
                System.out.println("嵌套2 objA");
            }
        }
    }
}
生产者&消费者:

生产和消费主要是包含了两类线程:

生产和消费也称为等待唤醒机制
生产者线程用于生产数据
消费者线程用于消费数据

实现:

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

Object类的等待和唤醒方法

方法名 说明
void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程

对象类:

public class Desk {
    // 定义一个标记判断食物的状态,为false表示没有食物,true代表有食物
    private boolean flag;

    // 食物的个数
    private int count;

    // 锁对象
    private final Object lock = new Object();


    public Desk() {
    }

    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }

    @Override
    public String toString() {
        return "Desk{" +
                "flag=" + flag +
                ", count=" + count +
                ", lock=" + lock +
                '}';
    }
}

生产者:

public class Cooker extends Thread {
    private Desk desk;

    public Cooker(Desk desk) {
        this.desk = desk;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (desk.getLock()) {
                if (desk.getCount() == 0) {
                    break;
                } else if (!desk.isFlag()) {
                    System.out.println("生产者正在生产食物");
                    // 有食物就改状态让消费者去消费
                    desk.setFlag(true);
                    desk.getLock().notifyAll();
                } else {
                    // 没有食物就线程等待唤醒
                    try {
                        desk.getLock().wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

消费者:

public class Foodie extends Thread {

    private Desk desk;

    public Foodie(Desk desk) {
        this.desk = desk;
    }


    @Override
    public void run() {

        while (true) {
            synchronized (desk.getLock()) {
                if (desk.getCount() == 0) {
                    break;
                    // 布尔类型变量的getset是is开头的
                } else if (desk.isFlag()) {
                    System.out.println("消费者消费了一个食物");
                    // 消费完毕,将标记设为false,当为false时可以让生产者去生产
                    desk.setFlag(false);
                    // 唤醒等待中的所有线程
                    desk.getLock().notifyAll();
                    // 消费一个减少一个
                    desk.setCount(desk.getCount() - 1);
                } else {
                    try {
                        // 线程等待唤醒
                        desk.getLock().wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

测试类:

public class Demo {
    public static void main(String[] args) {

        Desk desk = new Desk(false, 10);

        Foodie foodie = new Foodie(desk);
        Cooker cooker = new Cooker(desk);

        foodie.start();
        cooker.start();
    }
}
阻塞队列:

在这里插入图片描述

常见BlockingQueue:

ArrayBlockingQueue:底层是数组,有界
LinkedBlockingQueue:底层是链表,无界.但不是真正的无界,最大为int的最大值

BlockingQueue的核心方法:

方法名 说明
put(anObject): 将参数放入队列,如果放不进去会阻塞
take() 取出第一个数据,取不到会阻塞

阻塞队列等待&唤醒:

阻塞队列底层是有自动加锁的,但是运行起来,可能打印出的是存两个打印两个,这个是控制台打印的问题。

生产者者线程

public class Cooker extends Thread {
    private ArrayBlockingQueue<String> bd;

    public Cooker(ArrayBlockingQueue<String> bd) {
        this.bd = bd;
    }

    @Override
    public void run() {
        while (true) {
            try {
                bd.put("汉堡包");
                System.out.println("厨师放入一个汉堡包");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者线程

    private ArrayBlockingQueue<String> bd;

    public Foodie(ArrayBlockingQueue<String> bd) {
        this.bd = bd;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String take = bd.take();
                System.out.println("吃货将" + take + "拿出来吃了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

测试类:

  public class Demo {
      public static void main(String[] args) {
      	  // 创建阻塞队列对象,容量为1
          ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);

          Foodie f = new Foodie(bd);
          Cooker c = new Cooker(bd);

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

Java——多线程使用详解 的相关文章

  • ⛳ 面试题-单例模式会存在线程安全问题吗?

    目录 面试题 单例模式会存在线程安全问题吗 一 单例模式 简介 二 饿汉式 三 懒汉式 3 1 懒汉式 在调用 getInstance 的时候才创建对象 线程不安全 3 2 改造1 对懒汉式进行加锁改造 线程安全 3 3 改造2 对懒汉式继
  • Cpp关键字破解(三)【volatile】篇

    关键字总结 volatile 文章目录 关键字总结 volatile 0 前言 1 概念 2 作用 3 使用场景 4 volatile成员函数 5 代码体验 0 前言 参考几位前辈博客 汇总整理了一下 C 中volatile关键字的使用详解
  • qt多线程使用方式

    有5个方式 可以参考这个博客 Qt 中开启线程的五种方式 qt 线程 lucky billy的博客 CSDN博客 注 为了实现更加灵活的线程管理 因为这5种都有一些不方便之处 QThread需要子类化且不能传参 moveToThread不能
  • JUC编程

    1 JUC JUC就是java util concurrent工具包的简称 这是一个处理线程的工具包 JDK 1 5开始出现的 1 传统的synchronized public class Synchronized public stati
  • .Net/C#: 实现支持断点续传多线程下载的 Http Web 客户端工具类 (C# DIY HttpWebClient)

    选择自 playyuer 的 Blog Net C 实现支持断点续传多线程下载的 Http Web 客户端工具类 C DIY HttpWebClient Reflector 了一下 System Net WebClient 重载或增加了若干
  • java数据迁移程序

    环境 mysql 目标 亿级数据迁移 最终耗时 1 2小时 服务器更佳 建议在晚上或者没人访问的情况下操作 思路 1 不能一下将所有数据 导入到目标数据表 耗时太久 且占用资源 所有就用程序批量执行 每次执行一个范围段 比如第一个线程 1
  • GDB多线程调试常用命令

    gdb调试命令 step和next的区别 当前line有函数调用的时候 next会直接执行到下一句 step会进入函数 查看内存 gdb p a 打印变量地址 gdb x 0xbffff543 查看内存单元内变量 0xbffff543 0x
  • synchronized关键字修饰static方法和非static方法学习测试结论

    最近在学习研究synchronized关键字 发现有个疑问 在同一个类中 有多个sync方法 当线程调用其中的一个方法的时候 其他的线程能调用其他的sync方法么 为此做了简单的测试 详细的测试过程略过 读者可使用测试代码自行操作 得出结论
  • C++-std::unique_lock介绍和简单使用

    unique lock std unique lock比std lock guard更灵活 这种灵活性主要体现在以下几点 lock guard在构造时或者构造前 std adopt lock 就已经获取互斥锁 并且在作用域内保持获取锁的状态
  • [Java实现 Scoket实时接收Tcp消息 优化层层叠加]

    目录 前言 基础实现代码 描述 优化代码多线程处理客户端连接和消息接收 描述 再次优化异步实现 以下是使用 CompletableFuture 实现异步处理客户端请求的示例代码 描述 进一步优化的代码 Netty来实现Socket服务器 描
  • 多线程系列之——事件内核对象

    所有内核对象里面事件内核对象是最简单的一个 它包括一个使用计数 还有两个布尔值 一个布尔值用来表示事件是手动重置事件还是自动重置事件 另一个布尔值表示当前是否处于触发状态 当一个手动重置事件被触发的时候 所有等待该事件的线程都能变成调度状态
  • Java之单元测试(JUnit单元测试框架)

    一 概述 单元测试就是针对最小的功能单元编写测试代码 Java程序最小的功能单元是方法 所以单元测试就是针对Java方法的测试 进而检查方法的正确性 常规测试有什么问题 只有一个main方法 如果一个方法的测试失败了 其他方法会受到影响 无
  • Day8_8 Java学习之List集合类

    目录 一 List集合的概述 ArrayList集合类的概述 ArrayList集合的语法定义 ArrayList集合的常用方法 LinkedList集合概述 LinkedList集合的语法定义 LinkedList集合类常用方法 二 Se
  • 线程常见方法

    目录 线程常见的方法 设置优先级 Join方法 Sleep方法 setDaemon 线程常见的方法 starto 启动当前线程 表面上调用start方法 实际在调用线程里面的run方法 run 线程类继承Thread类或者实现Runnabl
  • QT实现多线程,以及子线程调用主线程方法与变量

    实现思路 第一步需要将子线程声明为主线程的友元类 第二步是将主线程类对象的地址通过信号槽传递给子线程中创建的对象 使得子线程能访问主线程的数据的 1 子线程 displayresult h 头文件 伪代码 include tabwindow
  • Java多线程 - 线程池常用容量设置

    线程执行方式 线程的执行是由CPU进行调度的 一个CPU在 同一时刻只会执行一个线程 操作系统利用了时间片轮转的方式 CPU给每个任务都服务一定的时间 然后把当前任务的状态保存下来 再加载下一个任务的状态后 继续服务下一个任务 任务的保存及
  • 02Linux下C语言锁的学习之Linux下的读写锁

    02Linux下C语言锁的学习之Linux下的读写锁 概述 下面的锁的意思均是代表读写锁 读写锁的特性 1 若一把锁被一个线程以读方式锁住 当其它线程以读方式上锁的话 那么可以上锁成功 2 若一把锁被一个线程以写方式锁住 当其它线程以读或者
  • JAVA实现简易HTTP服务器

    说实话 之前完全没有想过 我还能写出服务器 感觉服务器这么高端的东西 能会用就不错了 还能写 不吐槽了 开始了 这次的作业是搭建一个服务器 要能接收请求 并给浏览器返回正确响应 项目的下载地址 项目目标 实现一个简易的多线程服务器 可以处理
  • 多线程编程与性能优化

    引言 在上一篇的入门篇中 我们对Android线程的基础概念和多线程编程模型有了初步了解 本篇将深入探讨多线程编程技术和性能优化策略 以提升应用的效率和响应性 高级多线程编程技术 使用线程池管理线程 线程池是一组预先创建的线程 用于执行任务
  • TaskDecatator用法

    在Spring框架中 TaskDecorator 是一个接口 它可以用来自定义由 ThreadPoolTaskExecutor 或其他任务执行器管理的任务的装饰行为 这通常用于在执行任务之前和之后添加某些上下文相关的行为 比如设置线程上下文

随机推荐

  • Jtest简介

    Jtest是一个集成的 易于使用和Java单元测试工具 它能够自动测试任何Java类 JSP或部件 而不需要你编写一个测试用例 测试驱动程序和测试桩 只需一键 Jtest自动测试代码的构造 白盒测试 代码功能 黑盒测试 并维护代码的完整性
  • caffe convert_image 初略解析

    需要的基础知识 OpenCV 建议去看官网的图文教程 LevelDB http dblab cs toronto edu courses 443 2014 tutorials leveldb html 这个是我学习的教程 今天在看caffe
  • 时序预测

    时序预测 MATLAB实现贝叶斯优化LSTM时间序列预测 BO LSTM 预测效果 基本介绍 相对于模型的参数而言 Parameter 我们知道机器学习其实就是机器通过某种算法学习数据的计算过程 通过学习得到的模型本质上是一些列数字 如树模
  • uni-app富文本渲染方案rich-text、uparse、v-html简单解析

    uniapp语法 rich text 1 rich text是uni app的内置组件 提供了高性能的富文本渲染模式 2 API参考https uniapp dcloud io component rich text 3 rich text
  • 基于PHP的旅游管理系统

    系列文章目录 基于SpringBoot的高校在线答疑管理系统 基于SpringBoot的外卖点餐管理系统 基于SpringBoot的实验室管理系统 目录 系列文章目录 前言 一 相关技术 二 系统设计 1 系统体系结构 2 数据库设计原则
  • [人工智能-数学基础-1]:深度学习中的数学地图:计算机、数学、数值计算、数值分析、数值计算、微分、积分、概率、统计.....

    作者主页 文火冰糖的硅基工坊 https blog csdn net HiWangWenBing 本文网址 https blog csdn net HiWangWenBing article details 119710145 目录 1 为
  • java基础面试题系列(101-112)[完结撒花]

    20200718 by 1z 请说明sleep 和 wait 有什么区别 1 sleep 是Thread的成员方法而Wait 是Object的成员方法 2 sleep 没有释放锁 因此在sleep结束后依旧是原线程执行 而wait 释放了锁
  • pytorch(11)-- crnn 车牌端到端识别

    车牌图片端到端识别 一 前言 二 数据集处理 三 crnn模型文件 四 训练验证代码 五 测试代码 六 代码搬自 一 前言 本文主要记录了使用crnn 对车牌图片做端到端识别 即不用对车牌字符做逐个字符分割识别 车牌第一个字符为汉字 共有3
  • JS的map()方法会改变原始数组吗?

    map 为操作数组的一种方法 官方文档显示 map 方法返回一个新数组 数组中的元素为原始数组元素调用函数处理后的值 map 方法按照原始数组元素顺序依次处理元素 注意 map 不会对空数组进行检测 注意 map 不会改变原始数组 但是我在
  • kali 更新无法引导启动Windows11的解决办法

    摘要 出现的问题描述 更新kali之后 在你的Grub2引导中 没有Windows11的选项 但是通EFI引导是能正常启动系统的 保证Windows11的默认引导项是没有损坏 一 验证操作系统引导项 1 ls 在开机后的Grub引导页面 按
  • Unity 的 mathf.lerp

    Unity 的mathf lerp float a float b float t 是线性插值函数 a b的顺序很重要 如果a 10 b 0 则说明是从10到0的变化 如果是a 0 b 10 则说明是从0向10的变化 t的范围在0 1之间
  • Tomcat 8安装

    https www linuxidc com Linux 2017 10 147773 htm 前提 必须正确安装JDK 一 通过二进制包 tar gz 安装 下载 https mirrors tuna tsinghua edu cn ap
  • 分布式数据库需要考虑的(BigTable VS Dynamo)

    分布式数据库需要考虑的 BigTable VS Dynamo 在设计 评价分布式数据库的时候需要考虑一些最基本的特性 我想这些特性可能包括 1 存储系统 一种是类似BigTable将存储交给GFS去做 GFS会保证写入数据的完整 另外一种是
  • 应用多元统计分析(题解)

    题目 应用多元统计分析 北京大学出版社 第四章 回归分析 习题4 3 具体题目见下图 第一小问解答
  • Halcon深度学习常见问题及解决方法

    一 常见问题及解决办法 1 set dl model param DLModelHandle gpu GpuId GpuId 0 选中第一块显卡做深度学习训练 GpuId 1 选中第二块显卡做深度学习训练 类推 查询可用多显卡信息 quer
  • 使用EasyExce实现Excel文件解析

    hello 你好呀 我是灰小猿 一个超会写bug的程序猿 在上一篇文章中我和大家简单的介绍了使用easyexcel技术在有对象和无对象情况下实现Excel文件的写入操作 那么今天这一篇文章 我就继续来和大家讲一下 使用easyexcel技术
  • 入门力扣自学笔记211 C++ (题目编号:1781)

    1781 所有子字符串美丽值之和 题目 一个字符串的 美丽值 定义为 出现频率最高字符与出现频率最低字符的出现次数之差 比方说 abaacc 的美丽值为 3 1 2 给你一个字符串 s 请你返回它所有子字符串的 美丽值 之和 示例 1 输入
  • Football数据集可视化处理——gephi可视化处理数据

    1 football数据集的文件格式 根据如图所示football数据集和的文件格式如下所示 下图表示football数据集节点部分信息 下图表示football数据集边的部分信息 根据上述两个图中的格式对football数据集的格式介绍可
  • 浅谈Spring框架应用的设计模式(一)——工厂模式

    文章目录 前言 一 工厂模式介绍 1 简单工厂模式 1 静态工厂模式 2 利用反射机制实现的简单工厂 2 工厂方法模式 3 抽象工厂模式 二 Spring框架中工厂模式的重要应用 1 BeanFactory 2 FactoryBean 总结
  • Java——多线程使用详解

    多线程 多线程就是同时执行多个应用程序 需要硬件的支持 同时执行 不是某个时间段同时 cpu切换的比较快 所有用户会感觉是在同时运行 并发与并行 并行 parallel 指在同一时刻 有多条指令在多个处理器上同时执行 并行必须借助于多核cp