线程安全的部分知识

package cn.tan.phenomenon;

public class Main {
    // 定义一个共享的数据 —— 静态属性的方式来体现
    static int r = 0;

    // 定义加减的次数
    // COUNT 越大,出错的概率越大;COUNT 越小,出错的概率越小
    static final int COUNT = 1000_000_00;

    // 定义两个线程,分别对 r 进行 加法 + 减法操作
    static class Add extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < COUNT; i++) {
                r++;
            }
        }
    }

    static class Sub extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < COUNT; i++) {
                r--;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Add add = new Add();
        add.start();

        Sub sub = new Sub();
        sub.start();

        add.join();
        sub.join();

        // 理论上,r 被加了 COUNT 次,也被减了 COUNT 次
        // 所以,结果应该是 0
        System.out.println(r);
    }
}

以上代码是线程安全的吗?

单看代码是没有问题的,可是结果没能达到预期为0的结果

线程不安全的原因

 再看上面的例子,同时操作了一个静态属性r,因此就可能发生错误。

count越大,线程执行需要跨时间片的概率越大,导致中间出错的概率越大。

2.作为程序员如何考虑线程安全的问题


1.尽可能让几个线程之间不做数据共享,各干各的。就不需要考虑线程安全问题了
比如对一个数组的归并排序:4个线程虽然处理的是同一个数组,但提前划好范围,各做各的,就没问题了
2.如果非要有共享操作,尽可能不去修改,而是只读操作
static final int COUNT=;即使多个线程同时使用这个COUNT也无所谓的
3.一定会出现线程问题了,问题的原因从系统角度讲:
1.原子性被破坏了
2.由于内存可见性问题,导致某些线程读取到“脏(dity)”
3.由于代码重排序导致的线程之间关于数据的配合出问题了
原子性就是一个操作要么全部完成,要么都不完成。

 

锁:

synchronized


 关于类名.class的一个知识,每个被加载的类有且仅有一个class对象

 


synchronized修饰两种方法的区别

 互斥现象的产生

 (1)当多个线程都有加锁操作的时候(2)并且申请的是同一把锁的时候,锁的是同一个对象

现代java并发编程之JUC包

 关于lock一些案例的讲解

(1) 

public class Main1 {
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
        @Override
        public void run() {
            lock.lock();
            System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
        }
    }

    public static void main(String[] args) throws InterruptedException {
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        t.join();
    }
}

主线程加锁,之后子线程想要申请锁申请不到。

(2)

public class Main2 {
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
        @Override
        public void run() {
            lock.lock();
            System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
        }
    }

    public static void main(String[] args) throws InterruptedException {
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        TimeUnit.SECONDS.sleep(2);

        t.interrupt();      // 尝试让子线程停下来,但实际会徒劳无功

        t.join();
    }
}

这里让主线程睡两秒,尝试给子线程一个停止的信号,但是没有一个让他停止的动作,也没有用,锁仍然没有释放,仍然死锁。

(3)

public class Main3 {
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
        @Override
        public void run() {
            try {
                lock.lockInterruptibly();
                System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
            } catch (InterruptedException e) {
                System.out.println("收到停止信号,停止运行");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        TimeUnit.SECONDS.sleep(2);

        t.interrupt();

        t.join();
    }
}

这个案例和上一个的区别就是尝试让子线程停止,子线程用的是一个允许被中断的锁,这里再调用interrupt()方法就会以一个异常的形式来处理

(4)

package cn.tan.locks;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main4 {
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
        @Override
        public void run() {
            boolean b = lock.tryLock();
            if (b == true) {
                // 加锁成功了
                System.out.println("加锁成功");
                System.out.println("子线程进入临界区");     // 理论上,这句代码永远到达不了
            } else {
                System.out.println("加锁失败");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        TimeUnit.SECONDS.sleep(2);

        t.interrupt();      // 尝试让子线程停下来,但实际会徒劳无功

        t.join();
    }
}

这一个呢,用了trylock,尝试加锁,但是一直被主线程占用,所以返回一个false,加锁失败。

(5)

package cn.tan.locks;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main5 {
    private static final Lock lock = new ReentrantLock();

    static class MyThread extends Thread {
        @Override
        public void run() {
            boolean b = false;
            try {
                b = lock.tryLock(5, TimeUnit.SECONDS);
                if (b == true) {
                    // 加锁成功了
                    System.out.println("加锁成功");
                    System.out.println("子线程进入临界区");
                } else {
                    System.out.println("5s 之后加锁失败");
                }
            } catch (InterruptedException e) {
                System.out.println("被人打断了");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        lock.lock();

        MyThread t = new MyThread();
        t.start();

        TimeUnit.SECONDS.sleep(2);

        t.interrupt();
        lock.unlock();


        t.join();
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值