文档章节

第二章-对象及变量的并发访问-第一篇

简心
 简心
发布于 08/20 20:36
字数 3238
阅读 2
收藏 0

方法内部的变量为线程安全变量

“非线程安全”问题存在于“共享变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的。

package chaprer3;

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-08-04
 **/
public class Service {
    public void addI(String username) {
        try {
            int num = 0;
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

package chaprer3;

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-08-04
 **/
public class Thread1 extends Thread{
    private Service service;

    public Thread1(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.addI("a");
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-08-04
 **/
public class Thread2 extends Thread{
    private Service service;

    public Thread2(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.addI("b");
    }
}
package chaprer3;


/**
 * @program: demo
 * @description: demo\
 * @author: lee
 * @create: 2018-08-04
 **/
public class Run {
    public static void main(String[] args) {
        Service service = new Service();
        Thread1 thread1 = new Thread1(service);
        thread1.start();
        Thread2 thread2 = new Thread2(service);
        thread2.start();
    }

}

输出结果

a set over!
b set over!
b num=200
a num=100

共享变量非线程安全

多个线程同时访问一个对象的实例变量问题的时候会出现非线程安全问题

package chaprer3;

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-08-04
 **/
public class Service {
    int num = 0;
    public void addI(String username) {
        try {
           
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

输出结果

a set over!
b set over!
b num=200
a num=200

多个对象多个锁

多线程访问多个对象,则JVM会创建多个锁。

package chaprer3;

/**
 * @program: demo
 * @description: 用于测试的公共类
 * @author: lee
 * @create: 2018-08-13
 **/
public class PublicObject {
    private int num;

    public void addI(String username) {
        try {
            if ("a".equals(username)) {
                num = 100;
                Thread.sleep(3000);
                System.out.println("a set over");
            } else {
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(username + " num= " +  num);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

}    

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程1
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest1 extends Thread {
    private PublicObject object;

    public TheadTest1(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.addI("a");
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程2
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest2 extends Thread {
    private PublicObject object;

    public TheadTest2(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.addI("b");
    }
}    

package chaprer3;

import chapter2.Thread1;

/**
 * @program: demo
 * @description: 主线程
 * @author: lee
 * @create: 2018-08-13
 **/
public class MainRun {
    public static void main(String[] args) {
        PublicObject object = new PublicObject();
        PublicObject object1 = new PublicObject();
        TheadTest1 test1 = new TheadTest1(object);
        TheadTest2 test2 = new TheadTest2(object1);
        test1.start();
        test2.start();
    }
}    

输出结果

b set over
b num= 200
a set over
a num= 100

synchronized 方法与锁对象

synchronized占用的锁是对象锁

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程1
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest1 extends Thread {
    private PublicObject object;

    public TheadTest1(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.methodA();
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程2
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest2 extends Thread {
    private PublicObject object;

    public TheadTest2(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.methodA();
    }
}    
package chaprer3;

/**
 * @program: demo
 * @description: 用于测试的公共类
 * @author: lee
 * @create: 2018-08-13
 **/
public class PublicObject {
    public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}    

package chaprer3;

import chapter2.Thread1;

/**
 * @program: demo
 * @description: 主线程
 * @author: lee
 * @create: 2018-08-13
 **/
public class MainRun {
    public static void main(String[] args) {
        PublicObject object = new PublicObject();
        TheadTest1 test1 = new TheadTest1(object);
        TheadTest2 test2 = new TheadTest2(object);
        test1.start();
        test2.start();
    }
}    

输出结果

begin methodA threadName=Thread-0
begin methodA threadName=Thread-1
end
end

加上修饰以后

package chaprer3;

/**
 * @program: demo
 * @description: 用于测试的公共类
 * @author: lee
 * @create: 2018-08-13
 **/
public class PublicObject {
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}    

输出结果

begin methodA threadName=Thread-0
end
begin methodA threadName=Thread-1
end

或许这并不能体现对象锁,别着急接着看

package chaprer3;

/**
 * @program: demo
 * @description: 用于测试的公共类
 * @author: lee
 * @create: 2018-08-13
 **/
public class PublicObject {
    synchronized public void methodA() {
        try {
            System.out.println("begin methodA threadName="
                    + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void methodB() {
        System.out.println("begin methodB threadName="
                + Thread.currentThread().getName());
        System.out.println("end");

    }
}    

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程1
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest1 extends Thread {
    private PublicObject object;

    public TheadTest1(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.methodA();
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 测试的线程2
 * @author: lee
 * @create: 2018-08-13
 **/
public class TheadTest2 extends Thread {
    private PublicObject object;

    public TheadTest2(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.methodB();
    }
}    

输出结果

begin methodA threadName=Thread-0
end
begin methodB threadName=Thread-1
end

此时可以看出当某个线程先获得方法同步方法以后整个对象就已经被锁住了,其他线程需要等待同步方法执行完成以后才可以获得锁继续执行。

线程先持有object 对象的Lock 锁,另外一个线程可以以异步的方式调用object 对象中的非synchronized 类型的方法。

脏读

public class PublicObject {
    public String username = "A";
    public String password = "AA";
    synchronized public void setValue(String username, String password) {
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setValue method thread name="
                    + Thread.currentThread().getName() + " username="
                    + username + " password=" + password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getValue() {
        System.out.println("getValue method thread name="
                + Thread.currentThread().getName() + " username=" + username
                + " password=" + password);
    }
}

public class TheadTest1 extends Thread {
    private PublicObject object;

    public TheadTest1(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.setValue("B", "BB");
    }
}

public class TheadTest2 extends Thread {
    private PublicObject object;

    public TheadTest2(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.setValue("A", "AA");
    }
}   

输出结果

getValue method thread name=main username=B password=AA
setValue method thread name=Thread-0 username=B password=BB

出现脏读是因为public void getValue() 方法并不是同步的,所以可以在任意时候进行调用。解决办法当然就是加上同步synchronized 关键字,代码如下:

//加入了对象锁
    synchronized public void getValue() {
        System.out.println("getValue method thread name="
                + Thread.currentThread().getName() + " username=" + username
                + " password=" + password);
    }

当A 线程调用anyObject 对象加入synchronized 关键字的X 方法时,A 线程就获得了X方法锁,更准确地讲,是获得了对象的锁,所以其他线程必须等A 线程执行完毕才可以调用X 方法,但B 线程可以随意调用其他的非 synchronized 同步方法。

当A 线程调用anyObject 对象加入synchronized 关键字的X 方法时,A 线程就获得了X 方法所在对象的锁,所以其他线程必须等A 线程执行完毕才可以调用X 方法,而B 线程如果调用声明了synchronized 关键字的非X 方法时,必须等A 线程将X 方法执行完,也就是释放对象锁后才可以调用。这时A 线程已经执行了一个完整的任务,也就是说username 和password 这两个实例变量已经同时被赋值,不存在脏读的基本环境。

脏读一定会出现操作共享变量的情况下,这就是不同线程“争抢”共享变量的结果

synchronized 锁重入

“可重入锁”的概念是:自己可以再次获取自己的内部锁。

public class PublicObject {
    synchronized public void service1() {
        System.out.println("service1");
        service2();
    }
    synchronized public void service2() {
        System.out.println("service2");
    }
}    

“可重入锁”的概念是:自己可以再次获取自己的内部锁。比如有1 条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。

public class MainRun {
    public int i = 10;
    synchronized public void operateIMainMethod() {
        try {
            i--;
            System.out.println("main print i=" + i);
            Thread.sleep(100);
            this.operateIMainMethod();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}  

出现异常,锁自动释放

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

public class PublicObject {
    synchronized public void service1() {
       if (Thread.currentThread().getName() .equals("a")) {
           try {
               Thread.sleep(3000);
               //此处会发生异常
               Integer integer = Integer.parseInt("a");
               Thread.sleep(3000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       } else {
           System.out.println("thread current name is " + Thread.currentThread().getName());
       }
    }
}

public class TheadTest1 extends Thread {
    private PublicObject object;

    public TheadTest1(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.service1();
    }
}

public class TheadTest2 extends Thread {
    private PublicObject object;

    public TheadTest2(PublicObject object) {
        this.object = object;
    }

    @Override
    public void run() {
        this.object.service1();
    }
} 

public class MainRun {
    public static void main(String[] args) throws InterruptedException {
        PublicObject object = new PublicObject();
        TheadTest1 test1 = new TheadTest1(object);
        test1.setName("a");
        test1.start();
        Thread.sleep(1000);
        TheadTest1 test2 = new TheadTest1(object);
        test2.setName("b");
        test2.start();
    }
}    

输出结果

thread current name is b
Exception in thread "a" java.lang.NumberFormatException: For input string: "a"  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:492)
	at java.lang.Integer.parseInt(Integer.java:527)
	at chaprer3.PublicObject.service1(PublicObject.java:14)
	at chaprer3.TheadTest1.run(TheadTest1.java:18)

实验的结论就是出现异常的锁被自动释放了。

同步不具有继承性

当同步方法进行继承的时候,如果重写了方法,将不再同步,如果未进行重写仍旧保持同步。

public class MainClass {
    synchronized public void printString() throws InterruptedException {
        System.out.println("我已经进入了----" + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("我已经准备离开了----" + Thread.currentThread().getName());
    }
}

public class SubClass extends MainClass {

}

public class ThreadA extends   Thread {
    private SubClass subClass ;

    public ThreadA(SubClass subClass) {
        this.subClass = subClass;
    }

    @Override
    public void run() {
        try {
            subClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadB extends   Thread {
    private SubClass subClass ;

    public ThreadB(SubClass subClass) {
        this.subClass = subClass;
    }

    @Override
    public void run() {
        try {
            subClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果是

我已经进入了----Thread-0
我已经准备离开了----Thread-0
我已经进入了----Thread-1
我已经准备离开了----Thread-1

一旦进行重写

public class SubClass extends MainClass {
    @Override
    public void printString() throws InterruptedException {
        System.out.println("我已经进入了----" + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("我已经准备离开了----" + Thread.currentThread().getName());
    }
}

输出结果

我已经进入了----Thread-0
我已经进入了----Thread-1
我已经准备离开了----Thread-1
我已经准备离开了----Thread-0

synchronized 同步语句块

当两个并发线程访问同一个对象中的synchronized(this)同步代码块的时候,,一段时间内只有一个线程被执行,另一个线程必须等待当前线程执行完毕再执行。

public class MainClass {
    public void printString() throws InterruptedException {
        System.out.println("我已经进入了----" + Thread.currentThread().getName());
        synchronized (this) {
            Thread.sleep(5000);
        }
        System.out.println("我已经准备离开了----" + Thread.currentThread().getName());
    }
}  

package chapter2;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class RunClass {
    public static void main(String[] args) {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.start();
    }
}    

package chapter2;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadA extends   Thread {
    private MainClass mainClass ;

    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package chapter2;

import sun.applet.Main;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadB extends   Thread {
    private MainClass mainClass ;

    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果

我已经进入了----Thread-0
我已经进入了----Thread-1
我已经准备离开了----Thread-0
我已经准备离开了----Thread-1

同步代码块数据出现脏读

package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private String name1;
    private String name2;

    public void printString() throws InterruptedException {
        String a = Thread.currentThread().getName();
        String b = Thread.currentThread().getName() + "b";

        synchronized (this) {
            name1 = a;
            name2 = b;
        }
        //程序会出现脏读
        System.out.println(name1);
        System.out.println(name2);
    }
}    

package chapter2;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadA extends   Thread {
    private MainClass mainClass ;

    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package chapter2;

import sun.applet.Main;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadB extends   Thread {
    private MainClass mainClass ;

    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package chapter2;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class RunClass {
    public static void main(String[] args) {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.start();
    }
}    

输出结果

Thread-0
Thread-0b
Thread-1
Thread-1b
#或者
Thread-1
Thread-1b
Thread-1
Thread-1b 
#等等

将任意对象作为对象监视器

多线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块的时候,调用的效果就是按照顺序执行,也就是同步的,阻塞的。

synchronized同步方法

  • 对其他的synchronized同步方法或者同步代码块呈现阻塞状态。
  • 同一时刻只有一个线程可以执行synchronized同步方法中的代码。

同理synchronized(this)同步代码块也同样如此。

同步代码块锁住非this对象

package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private String name1;
    private String name2;
    private String anyString = new String();
    
    public void printString() throws InterruptedException {
        String a = Thread.currentThread().getName();
        String b = Thread.currentThread().getName() + "b";

        synchronized (anyString) {
            name1 = a;
            name2 = b;
        }
        //程序会出现脏读
        System.out.println(name1);
        System.out.println(name2);
    }
}    

如果不想出现脏读

package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private String name1;
    private String name2;
    private String anyString = new String();

    public void printString() throws InterruptedException {
        String a = Thread.currentThread().getName();
        String b = Thread.currentThread().getName() + "b";

        synchronized (anyString) {
            name1 = a;
            name2 = b;           
            System.out.println(name1);
            System.out.println(name2);
        }

    }
}    

锁住非this对象有一定的优点,与synchronized修饰的同步方法是异步的,不予synchronized(this)同步方法争抢this锁,可以大大提高效率。 当多个线程同时争抢synchronized(非this锁)的时候也是呈现同步效果。 同步代码块的另一种思考,其实要保证同步,只要保证同步同一个对象就行。如下所示

    package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private String name1;
    private String name2;
    private String anyString = new String();

    public void printString(Object object) throws InterruptedException {


        synchronized (anyString) {
            String a = Thread.currentThread().getName();
            String b = Thread.currentThread().getName() + "b";
            Thread.sleep(3000);
            name1 = a;
            name2 = b;
            System.out.println(name1);
            System.out.println(name2);
        }



    }
}

package chapter2;

import sun.applet.Main;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadA extends Thread {
    private Object object;
    private MainClass mainClass;

    public ThreadA(MainClass mainClass, Object object) {
        this.object = object;
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString(object);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package chapter2;

import sun.applet.Main;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class ThreadB extends Thread {
    private MainClass mainClass;
    private Object object;

    public ThreadB(MainClass mainClass, Object object) {
        this.object = object;
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        try {
            mainClass.printString(object);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package chapter2;

import sun.applet.Main;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass,object);
        threadA.start();
        Thread.sleep(1000);
        ThreadB threadB =new ThreadB(mainClass,object);
        threadB.start();
    }
}    

© 著作权归作者所有

共有 人打赏支持
简心
粉丝 28
博文 74
码字总数 144715
作品 0
济南
多线程基础必要知识点!看了学习多线程事半功倍

前言 不小心就鸽了几天没有更新了,这个星期回家咯。在学校的日子要努力一点才行! 只有光头才能变强 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 本文章的知识主要参考《Java并...

Java3y
04/23
0
0
《Effective Java》读书笔记

Effective Java读书笔记 第一章:引言 第二章:创建和销毁对象 第一条:创建对象的方式 1、构造器 2、 静态方法 3、构建器 第三条::强化singleton 1、三种方式实现单例 枚举实现(最佳方式)...

磊神Ray
2011/09/06
0
0
新书《高性能网站架构实战》前言 (部分)

我的系统运维观 我做系统运维工作也好几年了,基本上应付过各种各样的运维环境和工具。从最开始简单的各种应用部署,比如常用的LVS、HAproxy这样的负载应用、Apache、Nginx这类的Web应用,到...

单身贵族liu
2012/11/15
0
0
微信小程序教学第二章(含视频):小程序中级实战教程之预备篇 - 提取util公用方法

iKcamp官网:www.ikcamp.com 访问官网更快阅读全部免费分享课程:《iKcamp出品|全网最新|微信小程序|基于最新版1.0开发者工具之初中级培训教程分享》。 包含:文章、视频、源代码 第二章:...

iKcamp
2017/10/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

flume -- fileChannel简要分析其过程

flume之event写入FileChannel doPut(event)-->获取共享锁后[log.lockShared();]-->FlumeEventPointer ptr = log.put(transactionID, event); 此处的log.put即将transactionID及event进行后续......

-九天-
14分钟前
1
0
Linux与FreeBSD有什么区别?

基础 许多人所称的“Linux”实际上不是 Linux。Linux 从技术上说只是 Linux 内核,典型的 Linux 发行版则包括了 Linux 内核和许多软件。这是为什么 Linux 有时被称为 GNU/Linux。事实上,许多...

linux-tao
22分钟前
1
0
jQuery学习笔记180924

jQuery - AJAX 简介 什么是 AJAX? AJAX = 异步 JavaScript 和 XML(Asynchronous JavaScript and XML)。 简短地说,在不重载整个网页的情况下,AJAX 通过后台加载数据,并在网页上进行显示...

颖伙虫
35分钟前
1
0
springboot整合vue小试牛刀

序 本文主要研究一下如何在springboot工程整合vue maven <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-we......

go4it
37分钟前
1
0
使用python的profiler工具

主要用来检测python coding的执行时间 fly profiler

steel7c4
41分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部