一:多线程之线程安全

2019-05-27  本文已影响0人  一森

众所周知进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中肯定包含一个线程(main线程),一个进程中并且可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

那么什么是线程安全问题?

当N个线程同时操作同一个全局变量或静态变量,做写的操作(修改变量值)时,可能会发生数据冲突问题(资源抢占),也就是线程安全问题。但是做读操作时不会发生数据冲突问题。

举个雷子:

· 创建一个打印方法(打印 当前进程名称&当前耗时任务)


public static void printNum() {
    for (int i =0; i <5; i++) {
        System.out.println("线程名称:" + Thread.currentThread().getName() +" 耗时打印:" + i);
    }
}

·创建两个线程

public static class MyThread1 extends Thread {
        @Override
        public void run() {
            super.run();

            printNum();
        }
}

public static class MyThread2 extends Thread {
        @Override
        public void run() {
            super.run();

            printNum();
        }
}

·在main函数中开启两个线程

public static void main(String[] agrs) {
        MyThread1 myThread1 = new MyThread1();
        myThread1.start();

        MyThread2 myThread2 = new MyThread2();
        myThread2.start();
}

·看下输出

线程名称:Thread-0 耗时打印:0
线程名称:Thread-0 耗时打印:1
线程名称:Thread-0 耗时打印:2
线程名称:Thread-1 耗时打印:0
线程名称:Thread-0 耗时打印:3
线程名称:Thread-1 耗时打印:1
线程名称:Thread-0 耗时打印:4
线程名称:Thread-1 耗时打印:2
线程名称:Thread-1 耗时打印:3
线程名称:Thread-1 耗时打印:4

哎呀我去~交叉打印,我们来看下是为啥子:
原来线程的执行是CPU随机执行的,比如我们开启N个线程,这N个线程并不是同时执行的,而是CPU快速的在这N个线程之间切换执行,所以才会出现上面的情况,那么我们如何来解决这个问题呢?

在JDK1.5之前是通过关键字 synchronized 来进行代码同步的,也就是说当线程A执行该方法时就宣布了现在这个代码是我的了,如果 此时线程B想来执行这段代码,线程A说没门,让我干完再说;

·将printNum方法增加synchronized关键字

public static synchronized void printNum() {
        for (int i = 0; i < 5; i++) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 耗时打印:" + i);
        }
}

·看效果

线程名称:Thread-0 耗时打印:0
线程名称:Thread-0 耗时打印:1
线程名称:Thread-0 耗时打印:2
线程名称:Thread-0 耗时打印:3
线程名称:Thread-0 耗时打印:4
线程名称:Thread-1 耗时打印:0
线程名称:Thread-1 耗时打印:1
线程名称:Thread-1 耗时打印:2
线程名称:Thread-1 耗时打印:3
线程名称:Thread-1 耗时打印:4

在JDK1.5之后java加了个Lock锁,也是解决这个问题的,如果说synchronized是隐式的,那么Lock就是显示的,需要开发者进行上锁及解锁操作

看骚操作:

static Lock lock = new ReentrantLock();

public static void printNum() {
        lock.lock();
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程名称:" + Thread.currentThread().getName() + " 耗时打印:" + i);
            }
        } finally {
            lock.unlock();
        }
}

首先通过ReentrantLock创建了一个Lock,在代码前加上Lock.lock方法,那么unlock要放在finally中呢,原因是假如线程A执行代码出现异常unlock可能就不能被执行,也就是无法进行解锁,那么线程B就无法拿到锁,就无法进行代码执行了

看效果:

线程名称:Thread-0 耗时打印:0
线程名称:Thread-0 耗时打印:1
线程名称:Thread-0 耗时打印:2
线程名称:Thread-0 耗时打印:3
线程名称:Thread-0 耗时打印:4
线程名称:Thread-1 耗时打印:0
线程名称:Thread-1 耗时打印:1
线程名称:Thread-1 耗时打印:2
线程名称:Thread-1 耗时打印:3
线程名称:Thread-1 耗时打印:4

两种线程同步(抢占资源)的解决方式:

1、方法或代码块增加synchronized 关键字
2、方法前lock 方法后 unlock

上一篇 下一篇

猜你喜欢

热点阅读