我们使用多线程是解决 单线程的排队等待时间过长,但多线程随之而来了一个不速之客"数据不一致",共享资源导致的数据混乱--脏读,幻读,重复读 以及随之出现的各种我们经常听到经典问题,如:ABA问题

1.临界资源是什么?

设置一个变量 a = 0 , 创建2个线程 thread1 ,thread2, 线程做的事情是一样的,将变量a增加5万

这里的临界资源就是a (临界资源 : 被多个线程共享的资源)

补充:2019/02/21 :我在使用锁的时候,碰到一个问题,int/long...基础类型变量不能被锁,以前的理解是基础类型不是引用类型,没有继承Object超类,里面没有一些线程操作方法。 NOW 我觉得还是只看到了一个方面,还有共享这个点没涉及到,从JMM角度看,栈的私有的,堆是共享的,对象存储是在堆中,而基本类型的变量是存储在栈中的。   

有人会问:那锁对象的同时,基本类型变量怎么就锁住了呢 ?问得好,你很聪明。举个例子 -- 漂亮小姐姐支付宝里面的钱(基本类型变量),放在她的账号里面,你能用吗?不能,那你要是追到她了(获取"对象"),那你的她的不都是她的了么,你还能用吗?

java基础-7-C-多线程 -引子- 临界资源/锁

2. 临界资源会引起什么问题?

还是上面那个例子: 讲道理2个线程执行下来最终结果应该是10万,但是执行多次结果依旧不是预期的10万

java基础-7-C-多线程 -引子- 临界资源/锁java基础-7-C-多线程 -引子- 临界资源/锁java基础-7-C-多线程 -引子- 临界资源/锁java基础-7-C-多线程 -引子- 临界资源/锁...

要是出现在生产上,出现这个问题可能导致公司非常大的损失,我们怎么避免出现这种差错? NEXT

3. 解决临界资源问题的方法?

1.不使用多线程---这显然不现实,在能够用多线程提高性能的地方,弃而不用会被扣工资!!

2.多线程但线程之间不使用共享资源,那这就不存在临界资源这个问题了

3.使用线程安全的类型:AtomicInteger / AtomicReference...CurrentHashMap

4.保证同一时间临界资源只能被单个线程占用==》(1) synchronize同步锁  (2)ReentrantLock  ...

synchronize==》

java基础-7-C-多线程 -引子- 临界资源/锁

ReetrantLock , 这里我看到一道面试题说使用2个int类型简单实现读写锁的功能 《读写互斥,写写互斥,读读不互斥》==>

随手实现一个读写锁,不是非常严谨,但是基本点都有

/**
 * @author shengwuyou 用简单的2个int 类型实现一个读写锁
 * @version 创建时间:2019/2/20 17:44
 */

public class SelfReadWriterLock {
    //read 和 write 是分离的,同一时间只能由一种,所以读是1 写是-1 空闲时0
    private volatile int workType = 0;
    //workNum 表示锁的个数
    private volatile int workNum = 0;

    public void tryReadLock() throws InterruptedException {
        while (workType == -1){
            System.out.println("获取读锁失败");
            Thread.sleep(1000);
            //throw new RuntimeException("try get read Lock failed,now is writing");
        }synchronized (this){
            workType++;
            workNum++;
        }
    }

    public void tryWriteLock() throws InterruptedException {
        while (workType != 0){
            System.out.println("获取写锁失败");
            Thread.sleep(1000);
            //throw new RuntimeException("try get read Lock failed,now is writing");
        }
        synchronized (this){
            workType++;
            workNum++;
        }
    }


    public void unReadLock(){
        synchronized (this){
            if (workNum >0) {
                workNum--;
            }
            if (workNum == 0){
                workType = 0;
            }
        }
    }
    public void unWriteLock(){
        synchronized (this){
            if (workNum <0) {
                workNum--;
            }
            workType = 0;
        }
    }

}

用手写的读写锁去锁上面的临界资源例子:java基础-7-C-多线程 -引子- 临界资源/锁

这2中方式都能实现我们想要的目的。

4. 关于锁的使用中出现的问题: 死锁 / 活锁

    出现死锁的常见例子: 

  1. A线程已经占用a资源还需要b资源,B线程已经占用b资源还需要a资源
  2. 线程执行完成后才释放资源,无法外部释放线程获取的资源。
/**
 * @author shengwuyou 死锁的基本实现
 * @version 创建时间:2019/2/20 15:58
 */

public class ThreadSiSuoTest {
    /**
     * 这里使用Integer,需要注意 Integer变量在 [-128,127] 之间可以通过 synchronized(integer) 获取锁
     * 一旦超出需要设置成synchronized(integer.toString.intern())
     */
    private Integer a = 10;
    private Integer b = 20;

    class Run1 implements Runnable{
        @Override
        public void run() {
            synchronized (a){
                try {
                    System.out.println(Thread.currentThread().getName() + ": 拿到了 a 的锁了");
                    Thread.sleep(2000);
                    synchronized (b) {
                        System.out.println(Thread.currentThread().getName() + ": 拿到了 b 的锁了");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Run2 implements Runnable{
        @Override
        public void run() {
            synchronized (b){
                try {
                    System.out.println(Thread.currentThread().getName() + ": 拿到了 b 的锁了");
                    Thread.sleep(2000);
                    synchronized (a) {
                        System.out.println(Thread.currentThread().getName() + ": 拿到了 a 的锁了");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void sisuo(){
        Thread t1 = new Thread(new Run1());
        Thread t2 = new Thread(new Run2());
        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        ThreadSiSuoTest siSuoThreadTest = new ThreadSiSuoTest();
        siSuoThreadTest.sisuo();

    }
}

 活锁 --- 例子: 最常见的就是使用 CAS机制的情况下,当期望值 一直都和内存值不一致时,会不断的重试,形成一个活锁

死锁与活锁的区别:死锁中的线程是处于block(阻塞)状态,已经走不下去了 ,活锁中的线程是一直处于run 的状态 

补充:

  2019/02/27:

1.死锁检测: 在jdk1.7 之后已经给我们提供检测命令(比如有jstack)

相关文章:

  • 2021-06-01
  • 2022-12-23
  • 2022-12-23
  • 2021-05-16
  • 2022-12-23
  • 2021-05-11
猜你喜欢
  • 2021-07-15
  • 2021-12-26
  • 2021-11-24
  • 2022-12-23
  • 2022-12-23
  • 2021-11-26
  • 2021-05-07
相关资源
相似解决方案