synchronized关键字(一)

2023-10-31

一、线程安全和不安全

  • 非线程安全:在多个线程对同一个对象的实例变量进行并发访问时会出现值被更改、值不同步的情况
  • 线程安全:获得的实例变量的值是经过同步处理的,按照顺序执行,不会出现脏读情况

举个例子:5个销售员, 卖同一堆货物,每个销售员在卖出一个货品后,并不是立即知道当前货物剩余数量的,因为在他卖出的同时,可能其他销售员已经卖出好几个货品了,如果这个时候就减1,那么就会产生数据的不同步,可能售货员1计算剩余20个,售货员2计算剩余25个。因此,需要使5个卖货品的过程进行同步,即按顺序的方式进行减1,也就是每卖一次货物,要在当前剩余的数量上减1,即售货员1计算剩余20个,此时售货员2卖出一个,计算剩余数量20-1=19个

先看个线程不安全的例子

public class MyThread6_1 implements Runnable {

    private int count = 5;

    @Override
    public void run() {
        count--;
        System.out.println("由 " + Thread.currentThread().getName() +
                " 计算, count=" + count);
    }

    public static void main(String[] args) {
        MyThread6_1 thread = new MyThread6_1();
		// 5个线程共同调用线程 Thread6_1,同时共同修改变量 count
        Thread t1 = new Thread(thread);
        Thread t2 = new Thread(thread);
        Thread t3 = new Thread(thread);
        Thread t4 = new Thread(thread);
        Thread t5 = new Thread(thread);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

结果是:

Thread-0 计算, count=3Thread-2 计算, count=2Thread-1 计算, count=3Thread-4 计算, count=0Thread-3 计算, count=1

可以看到,Thread-0Thread-1 同时打印的 count 都是3,说明两个线程是一起执行 run 方法的,即同时减1,会产生非线程安全的问题,而我们理想的结果是,每个结果都是依次递减的,如果要解决这个问题,可以使用 synchronized 关键字来修饰方法

二、什么是synchronized同步方法

如果一个方法用 synchronized 修饰符来修饰,那么该方法称为同步方法,即如果有多个线程在同一时刻调用该方法,那么同一时刻只能有一个线程执行该方法,其他线程只能等待,等待前一个线程执行完同步方法之后再去执行该方法

与之对应的是异步方法,如果多个线程在同一个时刻调用该方法,那么同一时刻会有很多线程来调用该方法,不会存在后面的线程等待前面的线程执行完再执行的情况

锁机制

在 java 中,每个对象都拥有一个锁标记,也称为监视器,多线程同时访问某个对象时,线程只有获得了该对象的锁才能访问其中的方法

synchronized 可以在任意对象和方法上加锁,加锁的这段代码称为 互斥区 或者 临界区,当一个线程调用指定对象的同步方法时,线程首先要尝试去拿到调用对象的对象锁,如果能够拿到这把对象锁,那么这个线程就可以执行 synchronized 里面的代码;其他线程,因为没有拿到对象锁,因此不能访问调用对象里的同步方法,只能等待,只有等前一个线程执行完,它才会释放持有的对象锁,其他线程拿到锁后,才能调用对象的同步方法

我们再来看一个线程安全的例子,在 run 方法的前面加上 synchronized 关键字

public class MyThread6_1 implements Runnable {

    private int count = 5;

    //在 run 方法前面加上 synchronized 关键字
    @Override
    public synchronized void run() {
        count--;
        System.out.println("由 " + Thread.currentThread().getName() +
                " 计算, count=" + count);
    }

    public static void main(String[] args) {
        MyThread6_1 thread = new MyThread6_1();
		// 5个线程共同调用线程 Thread6_1,同时共同修改变量 count
        Thread t1 = new Thread(thread);
        Thread t2 = new Thread(thread);
        Thread t3 = new Thread(thread);
        Thread t4 = new Thread(thread);
        Thread t5 = new Thread(thread);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

结果是:

Thread-0 计算, count=4Thread-1 计算, count=3Thread-3 计算, count=2Thread-2 计算, count=1Thread-4 计算, count=0

可以看到,此时的结果没有在出现相同的 count 值的问题了。

当我们在方法前面加上 synchronized 关键字的时候,使得多个线程在执行 run 方法的时候,以排队的方式进行处理。这个例子中,5个线程同时调用同一个对象 thread,当第一个线程 Thread-0 执行 thread 对象的同步方法时,即持有了对象锁 thread,其他方法由于没有对象锁,只能等待 Thread-0 执行完同步方法并释放对象锁之后再执行,之后的4个线程都是按照这种模式来运行的

三、synchronized同步方法和线程安全

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

非线程安全存在于实例变量中的,如果是方法内部的变量,则是线程安全的,看个例子

class HashSelPrivateNum {

    public void addI(String username) throws InterruptedException {
        //该变量是 addI 方法内部的私有变量
        int num;
        
        if (username.equals("thread a")) {
            num = 100;
            System.out.println("a set over" + " " + System.currentTimeMillis());
            //使当前线程休眠
            Thread.sleep(2000);
        } else {
            num = 200;
            System.out.println("b set over" + " " + System.currentTimeMillis());
        }
        System.out.println(username + " num = " + num + " " + System.currentTimeMillis());
    }
}

创建线程 ThreadB 和线程 ThreadA

//创建线程类 ThreadB
class ThreadB extends Thread {
    private HashSelPrivateNum numRef;

    public ThreadB(HashSelPrivateNum numRef) {
        this.numRef = numRef;
    }

    @Override
    public void run() {
        try {
            numRef.addI("thread b");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//创建线程类 ThreadA
public class ThreadA extends Thread {

    private HashSelPrivateNum numRef;

    public ThreadA(HashSelPrivateNum numRef) {
        this.numRef = numRef;
    }

    @Override
    public void run() {
        try {
            numRef.addI("thread a");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HashSelPrivateNum hashSelPrivateNum = new HashSelPrivateNum();
        ThreadA threadA = new ThreadA(hashSelPrivateNum);
        threadA.start();

        ThreadB threadB = new ThreadB(hashSelPrivateNum);
        threadB.start();
    }
}

结果是:

a set over 1540024730771
b set over 1540024730771
thread b num = 200 1540024730772
thread a num = 100 1540024732772

可以看到,此时虽然类 HashSelPrivateNum 的方法 addI 不是同步方法,且不是按照代码的顺序输出的,但是当线程 A 和线程 B 调用的时候,因为此时的变量 num 是方法内部的变量,每个线程都有自己的一个变量 num,因此,每个线程只能对自己对应的那个变量 num 赋值,所以不会造成数值的覆盖或者重复等,是线程安全的,这是变量 num 是方法的局部变量这个性质造成的

2.2 实例变量为非线程安全

如果多个线程同时访问1个对象中的实例变量,即公共的变量,则会出现非线程安全

还是上面的方法,我们将 HashSelPrivateNum 类的 num 变量变为实例变量

class HashSelPrivateNum {

    //变成了公共变量
    private int num = 0;

    public void addI(String username) throws InterruptedException {
        if (username.equals("thread a")) {
            num = 100;
            System.out.println("a set over" + " " + System.currentTimeMillis());
            //使当前线程休眠
            Thread.sleep(2000);
        } else {
            num = 200;
            System.out.println("b set over" + " " + System.currentTimeMillis());
        }
        System.out.println(username + " num = " + num + " " + System.currentTimeMillis());
    }
}

线程 A 和线程 B 的方法和上面一样,结果如下:

a set over 1540024616286
b set over 1540024616286
thread b num = 200 1540024616286
thread a num = 200 1540024618287

此时变量不再是私有变量了,从输出可以看到,除了顺序不是按照代码顺序之外,此时的共有 num 变量也出现了重复的情况,究其原因,就是线程 thread a 执行完 if 语句还没有输出的时候,线程 thread b 也进入这个方法,执行 if 语句并且重新为实例变量 num 赋值为 200,然后 thread b 输出变量的值就是200,然后线程 thread a 再执行,因为共有变量 num 已经修改过了,因此线程 thread a 输出变量的值还是 200,此时输出的是 脏数据,即修改之后的数据,因此出现了 非线程安全 的问题

对于这个问题,我们只需要在 addI 方法前面添加 synchronized 关键字即可

class HashSelPrivateNum {

    private int num = 0;

    public synchronized void addI(String username) throws InterruptedException {
        if (username.equals("thread a")) {
            num = 100;
            System.out.println("a set over" + " " + System.currentTimeMillis());
            Thread.sleep(2000);
        } else {
            num = 200;
            System.out.println("b set over" + " " + System.currentTimeMillis());
        }
        System.out.println(username + " num = " + num + " " + System.currentTimeMillis());
    }
}

线程 A 和线程 B 的代码同上,main 方法也同上,结果是:

a set over 1540025424653
thread a num = 100 1540025426655
b set over 1540025426655
thread b num = 200 1540025426655

可以看到,线程 thread a 先执行同步方法 addI() ,同时拥有了对象锁 hashSelPrivateNum,那么线程 thread-b 只能等待 thread a 执行完同步方法,释放对象锁之后,才可以获取对象锁,然后执行该对象的同步方法

此时addI 是同步方法,哪个线程先拿到对象锁,就先执行同步方法,没有拿到对象锁的线程,只能等待前者执行完同步方法之后才可以执行,此时就不会发生数据的脏读

得出结论:在两个线程访问同一个对象中的同步方法时一定是线程安全的

三、多个对象多个锁

创建 HashSelPrivateNum 类,其中的方法 addI 是同步方法

class HashSelPrivateNum {

    //如果变量不是方法的私有变量,此时变成了公共变量,则有可能出现线程安全问题,此时需要加 synchronized 关键字
    private int num = 0;

    public synchronized void addI(String username) throws InterruptedException {
        //该变量是 addI 方法内部的私有变量,此时不加 synchronized 关键字也不会存在线程安全问题
//        int num;

        if (username.equals("thread a")) {
            num = 100;
            System.out.println("a set over" + " " + System.currentTimeMillis());
            //使当前线程休眠
            Thread.sleep(2000);
        } else {
            num = 200;
            System.out.println("b set over" + " " + System.currentTimeMillis());
        }
        System.out.println(username + " num = " + num + " " + System.currentTimeMillis());
    }
}

创建线程 A1 和线程 B1,同时使用这2个线程访问2个不同的对象

class ThreadB1 extends Thread {

    private HashSelPrivateNum numRef;

    public ThreadB1(HashSelPrivateNum numRef) {
        this.numRef = numRef;
    }

    @Override
    public void run() {
        try {
            numRef.addI("thread b");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadA1 extends Thread {

    private HashSelPrivateNum numRef;

    public ThreadA1(HashSelPrivateNum numRef) {
        this.numRef = numRef;
    }

    @Override
    public void run() {
        try {
            numRef.addI("thread a");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        //创建两个对象
        HashSelPrivateNum numRef1 = new HashSelPrivateNum();
        HashSelPrivateNum numRef2 = new HashSelPrivateNum();
		//两个线程访问两个不同的对象
        ThreadA1 threadA1 = new ThreadA1(numRef1);
        threadA1.start();

        ThreadB1 threadB1 = new ThreadB1(numRef2);
        threadB1.start();
    }
}

结果是:

a set over 1540027131988
b set over 1540027131988
thread b num = 200 1540027131989
thread a num = 100 1540027133989

可以看到,多个线程访问多个对象,该实例创建了 2 个 HashSelPrivateNum 类的对象,就产生了 2 个对象锁,当线程 Thread A1 执行 synchronized 方法 addI(),便持有该方法所属对象 numRef1 的锁,此时线程 B 并不用等待,因为持有对象 numRef2 的锁,与 numRef1 锁无关。此时方法是异步执行的

当用两个线程分别访问同一个类的不同实例的时候,虽然类中方法是同步方法,但是输出的结果是 异步 的,即不是按照正确的顺序来执行的

关键字 synchronized 取得的锁都是对象锁,而不是把一段代码或者方法当作锁,之前的那个例子,是 多个线程访问同一个对象,此时哪个线程先执行带有 synchronized 关键字的方法,哪个线程就持有了该方法所属对象的锁,此时其他线程只能等待,等待这个持有对象锁的线程执行完 run 方法之后,在继续执行。因此这个执行和等待的过程是严格按照顺序,即 同步 的方式来进行的

该实例创建了 2 个 HashSelPrivateNum 类的对象,就产生了 2 个对象锁,虽然有锁,但都是各自的,即自己执行自己的,不需要等待其他线程完成才能执行,也不会产生数据的重复

四、脏读

即两个线程对同一个对象中的数据进行修改时发生的数据交叉或者重复的问题

public class PubVar {

    public String username = "AAA";
    public String password = "123456";

    synchronized public void setValue(String username, String password) {
        try {
            this.username = username;
            System.out.println(Thread.currentThread().getName() + " setValue begin"
                    + " user = " + this.username + " pas = " + this.password + " " + System.currentTimeMillis());
            //在给 password 赋值的时候将当前线程睡眠 2s
            Thread.sleep(2000);
            this.password = password;
            System.out.println(Thread.currentThread().getName() + " setValue end"
                    + " user = " + this.username + " pas = " + this.password + " " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void getValue() {
        System.out.println(Thread.currentThread().getName() + " getvalue"
                + " user = " + username + " pas = " + password + " " + System.currentTimeMillis());
    }

}

创建线程 ThreadA4

public class ThreadA4 extends Thread {

    private PubVar pubVar;

    public ThreadA4(PubVar pubVar) {
        this.pubVar = pubVar;
    }

    @Override
    public void run() {
        pubVar.setValue("BBB","654321");
    }

    public static void main(String[] args) throws InterruptedException {
        PubVar pubVar = new PubVar();
        ThreadA4 threadA4 = new ThreadA4(pubVar);
        threadA4.start();
        //使得 main 线程睡眠 2s
        Thread.sleep(2000);
        pubVar.getValue();
    }
}

结果是:

Thread-0 setValue begin user = BBB pas = 123456 1540030412129
main getvalue user = BBB pas = 123456 1540030414129		//该数据的 pas 是还没有赋值之前的值
Thread-0 setValue end user = BBB pas = 654321 1540030414129

因为 PubVar 类中的 getValue 方法并不是同步的,索引可以在任意时刻调用,因此, setValue 方法还没有给 password 变量赋完值,就直接执行 getValue 方法了,此时输出的数据是只有一半是正确的

如果我们给 getValue 方法加上 synchronized 关键字,这个时候 setValue 和 getValue 就依次执行了,结果是:

Thread-0 setValue begin user = BBB pas = 123456 1540040547078
Thread-0 setValue end user = BBB pas = 654321 1540040549080
main getvalue user = BBB pas = 654321 1540040549080

分析一下两个过程,首先,线程 ThreadA4 获得了对象 pubVar 的锁,然后在线程 ThreadA4 中执行对象所在类的同步方法 setValue,这个时候,其他线程只有等线程 ThreadA4 执行完 setValue 后才能执行这个方法

  • 对于第 1 个例子,其中的 getValue 不是同步方法,同时也是线程 main 调用的,这个时候,因为不是同步方法,所以线程 main 可以在任意时刻调用这个非同步方法(没有 synchronized 修饰),也就是说,可能向上面一样,赋值到一半就输出了,也有可能全部赋值完再输出,只是这个时候和对象锁无关了
  • 对于第 2 个例子,其中 getValue 是同步方法,此时类 PubVar 中就有两个同步方法,因为 setValue 正在执行,即线程 ThreadA4 持有该方法所在对象 pubVar 的对象锁,而线程 main 也要调用该对象的另一个同步方法 getValue,所以线程 main 必须等待线程 ThreadA4 执行完 setValue 方法并且释放对象锁之后才能调用 getValue 方法。这时线程 ThreadA4 已经按照代码执行顺序对变量 username 和 password 进行了赋值,最好线程 main 再调用方法进行输出,这个时候不存在脏读的情况

简单的说

  1. 一个对象里面,如果只有一个同步方法 X,如果它被一个线程 A 调用,即线程 A 获取了 X 所在对象的锁,那么其他线程必须等到线程 A 执行完方法 X 之后才能调用方法 X,但是其他线程却可以随意调用对象里的非同步方法,与对象锁无关,这个时候会出现脏读
  2. 一个对象里面,如果有一个同步方法 X,它被一个线程 A 调用,即线程 A 获取了 X 所在对象的锁,同时还有一个同步方法 Y,它被另一个线程 B 调用,此时线程 B 不能随意执行方法 Y 了,必须等到线程 A 将方法 X 执行完,释放对象锁之后才能执行方法 Y,这个时候与对象锁有关,不会出现脏读

五、锁重入

关键字 synchronized 具有锁重入的功能,即,当一个线程得到一个对象锁之后,再次请求次对象锁是可以再次得到该对象的锁的。因此,可以在一个 synchronized 方法内部调用本类的其他 synchronized 方法,这个时候用于可以得到内部锁

class Service {

    synchronized public void service1() {
        System.out.println(Thread.currentThread().getName() + " service");
        //调用同步方法 service2()
        service2();
    }

    synchronized public void service2() {
        System.out.println(Thread.currentThread().getName() + " service2");
        //调用同步方法 service3()
        service3();
    }

    synchronized public void service3() {
        System.out.println(Thread.currentThread().getName() + " service3");
    }

}

public class ThreadA5 extends Thread {

    @Override
    public void run() {
        //线程 ThreadA5 得到了 service 对象锁
        Service service = new Service();
        //通过 service 对象调用同步方法 service1()
        service.service1();
    }

    public static void main(String[] args) {
        ThreadA5 threadA5 = new ThreadA5();
        threadA5.start();
    }

}

结果是:

Thread-0 service
Thread-0 service2
Thread-0 service3

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

可重入锁也可以用于继承关系中,即子类可以调用父类的 synchronized 方法但是得注意的是:同步不具有继承性。看个例子:

class Main2 {
	//父类的 serviceMethod() 是同步方法
    synchronized public void serviceMethod() {
        try {
            System.out.println("int main 下一步 sleep begin "
                    + Thread.currentThread().getName() + " time = "
                    + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("int main 下一步 sleep end "
                    + Thread.currentThread().getName() + " time = "
                    + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Sub2 extends Main2 {
	
    //子类继承父类的同步方法
    @Override
    public void serviceMethod() {
        try {
            System.out.println("int sub 下一步 sleep begin "
                    + Thread.currentThread().getName() + " time = "
                    + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("int sub 下一步 sleep end "
                    + Thread.currentThread().getName() + " time = "
                    + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadB8 extends Thread {

    private Sub2 sub2;

    public ThreadB8(Sub2 sub2) {
        this.sub2 = sub2;
    }

    @Override
    public void run() {
        sub2.serviceMethod();
    }
}

public class ThreadA8 extends Thread{

    private Sub2 sub2;

    public ThreadA8(Sub2 sub2) {
        this.sub2 = sub2;
    }

    @Override
    public void run() {
        sub2.serviceMethod();
    }

    public static void main(String[] args) {
        Sub2 sub2 = new Sub2();
        ThreadA8 threadA8 = new ThreadA8(sub2);
        threadA8.setName("AA");
        threadA8.start();

        ThreadB8 threadB8 = new ThreadB8(sub2);
        threadB8.setName("BB");
        threadB8.start();
    }
}

结果是:

int sub 下一步 sleep begin AA time = 1540131922832
int sub 下一步 sleep begin BB time = 1540131922834
int sub 下一步 sleep end AA time = 1540131925834
int sub 下一步 sleep end BB time = 1540131925835

通过结果发现,两个线程调用子类的方法后,不是同步执行的,说明子类继承父类的同步方法并不具有同步性,如果在子类的方法前面加上 synchronized 关键字,此时结果是:

int sub 下一步 sleep begin AA time = 1540132164713
int sub 下一步 sleep end AA time = 1540132167713
int sub 下一步 sleep begin BB time = 1540132167713
int sub 下一步 sleep end BB time = 1540132170714

此时方法同步了,说明同步不能被继承,得在子类的方法中添加 synchronized 关键字才可以得到同步方法

六、参考

《Java多线程编程核心技术》

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

synchronized关键字(一) 的相关文章

  • Qt的Tcp服务器多线程编程-附带代码展示

    Qt的Tcp服务器多线程编程 附带代码展示 该程序主要实现tcp服务器如何使用多线程的方式来连接多个客户端 此文章没有实现客户端的多线程编程 创建子线程时需要注意的点 1 子线程与主线程之间交互数据时 应采用信号槽的方式 2 子线程中实例化
  • C++多线程(七):unique_lock详解

    目录 unique lock取代lock guard unique lock的第二个参数 std adopt lock std try to lock std defer lock unique lock的成员函数 成员函数lock 成员函
  • Java的线程同步 & 并发操作

    并发 CUP在同一时间或同一时段内只能执行一件事情 而不同时件执行时 切换得十分快速 因为CUP的频率非常高 切换的速度人根本感受不出来 同步 同步是多个任务进行时 按照一定的规律进行着 线程并发 同一时间间隔中 有多个线程在同时执行 就是
  • 对聊天室的优化&常用参数配置

    优化处1 编码和解码 编码解码用的是JDK 对象与数组的转换 这种虽然简单 但是效率不高 现在需要支持更多的序列化算法 就需要改进 抽取一个接口 Serializer 用以支持 序列化和 反序列化 package com zhao prot
  • 线程的优先级

    package com kuang Demo05 测试线程的优先级 public class TestPriority public static void main String args System out println Threa
  • Disruptor 详解

    Disruptor 详解 想了解一个项目 最好的办法就是 把它的源码搞到本地自己捣鼓 在网上看了 N 多人对 Disruptor 速度的吹捧 M 多人对它的机制分析 就连 Disruptor 官方文档中 也 NB 哄哄自诩 At LMAX
  • 多线程实现事务回滚

    多线程实现事务回滚 特别说明CountDownLatch CountDownLatch的用法 CountDownLatch num 简单说明 主线程 mainThreadLatch await 和mainThreadLatch countD
  • 多线程太可怕了

    今天发现了一个多线程引起的bug 然后进一步体会到 这东西太容易出问题了 首先要说明的是 出问题的代码可不是一般人写的 是由一个叫EPAM systems的世界知名外包公司的人写的 这些java程序员个个经验丰富 心高气傲 貌似base在乌
  • java中什么是并发,如何解决?

    多个进程或线程同时 或着说在同一段时间内 访问同一资源会产生并发问题 银行两操作员同时操作同一账户就是典型的例子 比如A B操作员同时读取一余额为1000元的账户 A操作员为该账户增加100元 B操作员同时为该账户减去 50元 A先提交 B
  • 【Java】网络编程——多线程下载文件

    前言 多线程下载文件 比单线程要快 当然 线程不是越多越好 这和获取的源文件还有和网速有关 原理 在请求服务器的某个文件时 我们能得到这个文件的大小长度信息 我们就可以下载此长度的某一个片段 来达到多线程下载的目的 每条线程分别下载他们自己
  • [QT编程系列-25]:多线程机制 - QThread和MoveToThread简介

    目录 第1章 简介 1 1 多线程的目的 1 2 QThread多线程使用方法 1 3 QT支持多线的步骤 第2章 QThread 2 1 概述 2 2 moveToThread 第1章 简介 1 1 多线程的目的 QThread类提供了一
  • 线程常见方法

    目录 线程常见的方法 设置优先级 Join方法 Sleep方法 setDaemon 线程常见的方法 starto 启动当前线程 表面上调用start方法 实际在调用线程里面的run方法 run 线程类继承Thread类或者实现Runnabl
  • Linux系统编程:多线程交替打印ABC

    引言 分享关于线程的一道测试题 因为网上基本都是Java的解决方法 决定自己写一篇来记录一下线程的学习 问题描述 编写一个至少具有三个线程的程序 称之为线程 A B 和 C 其中线程 A 输出字符 A 线程 B 输出字符 B 线程 C 输出
  • 线程池用例

    线程池逻辑类 public class TaskExecutorService private final ExecutorService pool private final ThreadPoolExecutor pool private
  • QT实现多线程,以及子线程调用主线程方法与变量

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

    线程执行方式 线程的执行是由CPU进行调度的 一个CPU在 同一时刻只会执行一个线程 操作系统利用了时间片轮转的方式 CPU给每个任务都服务一定的时间 然后把当前任务的状态保存下来 再加载下一个任务的状态后 继续服务下一个任务 任务的保存及
  • 阿里巴巴开发手册-并发处理

    强制 获取单例对象要线程安全 在单例对象里面做操作也要保证线程安全 说明 资源驱动类 工具类 单例工厂类都需要注意 强制 线程资源必须通过线程池提供 不允许在应用中自行显式创建线程 说明 使用线程池的好处是减少在创建和销毁线程上所花的时间以
  • Java线程(Thread)生命周期的6种状态

    当线程被创建并启动以后 它既不是一启动就进入了执行状态 也不是一直处于执行状态 在线程的生命周期中 可能处于不同的状态 java lang Thread State 列举出了这6种线程状态 线程状态 导致状态发生条件 New 新建 线程刚被
  • ScheduledThreadPoolExecutor周期定时任务异常处理踩坑的问题!!

    问题原因 在公司写项目的时候 有一个周期定时任务的需求 就想着阿里巴巴开发手册里不是说不能用Executors去创建线程池 因为存在如下问题 FixedThreadPool和SingleThreadPool 允许的请求队列长度为 Integ
  • TaskDecatator用法

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

随机推荐

  • DATAX 数据同步 My SQL->Hive

    DATAX 数据同步 My SQL gt Hive 安装 DATAX官方地址 https github com alibaba DataX DATAX WEB官方地址 https github com WeiYe Jing datax we
  • 在嵌入式设备中不创建swap分区的原因何在

    我们知道 在linux中有一个很重要的概念 虚拟内存 当物理内存不够时 可以将内存中一些数据存储到硬盘上 而让出物理内存 我们一般在PC上安装linux时 会创建一个swap分区 其大小一般设置为内存的2倍 而我们在嵌入式设备中 却看不到该
  • 面试时,如何向HR解释自己频繁跳槽?

    有数据显示 现在的职场人 跳槽越来越频繁 95后平均7个月就离职 对于面试官来说 一个跳槽过于频繁的人总是存在潜在风险 比如抗压力差 稳定性不好 心不定这山望着那山高 职业规划不清晰等等 我一直强调一个观点 职场人跳槽 应该是为了下一步有更
  • git配置用户名和密码_IDEA配置码云教程

    第一步 先在电脑上安装git exe 否则以后会出现错误 官方下载地址 https www git scm com downloads 官方中文教程 https git scm com book zh v2 点击自动下载 然后下载好后 选择
  • linux几种压缩命令的简单使用

    在CentOS 7中 常用的解压缩命令是tar gzip和bzip2 压缩命令是tar gzip bzip2和zip 以下是这些命令的使用方法 1 tar命令 tar是常用的归档和压缩工具 可以用来打包 压缩和解压文件 常用的选项有 c 创
  • 《深入浅出React和Redux》(1) - React基础

    create react app React技术依赖的技术栈比较多 比如 转译JavaScript代码需要使用Babel 模块打包工具要使用Webpack 定制build过程需要grunt或者gulp create react app命令可
  • 【Spring容器】项目启动后初始化数据的两种实践方案

    早期业务紧急 没有过多的在意项目的运行效率 现在回过头看走查代码 发现后端项目 Spring MVC MyBatis 在启动过程中多次解析mybatis的xml配置文件及初始化数据 对开发阶段开发人员反复启停项目造成很大的时间浪费 也即是下
  • java设计模式之享元模式通俗易懂

    什么是享元模式 为什么要使用享元模式 享元模式属于结构型设计模式 享元模式主要是为了解决大量对象创建后 增大了系统的资源开销 为了解决此问题 该模式通过共享对象的方式实现 既然是共享肯定有一定结构和策略 通俗易懂的举个例子 我们生活当中需要
  • C#元组学习笔记

    元组功能提供了简洁的语法来将多个数据元素分组成一个轻型数据结构 下面的示例演示了如何声明元组变量 对它进行初始化并访问其数据成员 double int t1 4 5 3 Console WriteLine Tuple with elemen
  • Vue-全局配置axios

    Vue 全局配置axios 1 安装axios npm i axios 2 在main js 引入axios import axios from axios 3 全局配置axios的请求根路径 axios defaults baseURL
  • OAuth原理,图文并茂,通俗易懂

    步骤2请求OAuth登录页 慕课的服务器请求腾讯服务器 步骤3使用第三方账号登录并授权 步骤4返回登录结果 拿到了加密后的code参数 有了code基本上可以确定 用户输入的QQ号和密码 是匹配的 也就是说登录是成功的 但还不够出于安全性的
  • 服务器磁盘如何新建文件夹,如何使用网络硬盘

    网络硬盘是将OA服务器上某一个已经存在的文件夹开放并允许用户通过IE进行共享访问 是一种重要的文件共享方式 类似于文件柜 又区别于文件柜 网络硬盘的存储是将OA服务器上的某个物理存在的文件夹 设置为网络硬盘 换言之 是将文件存储于服务器上特
  • Android TreeView 树形组织结构 带复选框

    之前做项目的时候做人员组织架构时候需要用到 同样可以用于目录视图 简单搜了一下没有合适的 只找到一个基础的有瑕疵的树形结构 就在基础上改了增加了复选框以及简化了部分代码 下面上演示效果图 时长25秒 手机卡见谅 复选框有两种设计模式 1 子
  • SpringBoot 更优雅的实现接口操作日志获取

    SpringBoot 更优雅的实现接口操作日志获取 使用注解 延迟到子类处理的方式实现解耦合 1 定义 Oplog 系统操作日志注解 author chenyusheng create 2023 6 28 9 56 description
  • 字符串系列题目(C++)

    参考链接 https leetcode cn com problems fan zhuan dan ci shun xu lcof solution yi ge mo ban shua bian suo you zi fu chu x6vh
  • 五大常用算法之二:动态规划算法

    一 基本概念 动态规划过程是 每次决策依赖于当前状态 又随即引起状态的转移 一个决策序列就是在变化的状态中产生出来的 所以 这种多阶段最优化决策解决问题的过程就称为动态规划 二 基本思想与策略 基本思想与分治法类似 也是将待求解的问题分解为
  • Spring基础入门2 - IoC容器

    在上文介绍了Spring IoC容器的基本用法 下面做一个稍微复杂点的例子 继续学习Spring的IoC和AoP的 实际 使用 我们假设有一个在线商城OnlineStore 在线商城需要提供一些银行相关服务 假设 因为银行服务的实现是容易变
  • Qt 基于元对象系统实现反射

    背景 C 不支持反射 但使用 Qt 的元对象系统可以实现反射机制 使用反射创建对象 概述 使用 Qt 的元对象系统 可以使用反射来创建对象 需要把构造函数用 Q INVOKABLE 进行标记 类定义示例 class demoClass pu
  • 微信小程序 全局路由拦截

    1 微信小程序 全局路由拦截 utils filter js function loginCheck pageObj if pageObj onLoad let onLoad pageObj onLoad 使用onLoad的话需要传递opt
  • synchronized关键字(一)

    一 线程安全和不安全 非线程安全 在多个线程对同一个对象的实例变量进行并发访问时会出现值被更改 值不同步的情况 线程安全 获得的实例变量的值是经过同步处理的 按照顺序执行 不会出现脏读情况 举个例子 5个销售员 卖同一堆货物 每个销售员在卖