美文网首页
JAVA-线程安全

JAVA-线程安全

作者: 超人TIGA | 来源:发表于2021-03-12 22:18 被阅读0次

程序都避免不了使用线程,但是当不同的线程同时操作同一个变量、同一个对象时,如果不进行控制,那么程序的结果就极大可能不是我们想要的。

例如:有一个变量a,初始值为0;
线程A、线程B都分别对变量a进行累加100次,每次增加1;
如果不加任何控制,那最后的结果就不会是200了。
这种时候,我们就需要对相应部分的代码进行控制,就是加个锁。

什么是锁?

①类锁
以一个类来作为锁,常见于单例模式,还有修饰静态的方法。
因为方法是静态的,也就是说这个类和方法不需要经过实例化就可以调用,所以如果要加锁控制,同样也不能要求实例化,所以叫类锁。

/**
 * synchronized 内置🔒  JDK提供的 内部已经封装
 */
public class GPSEngine {

    private static GPSEngine gpsEngine;

    public static synchronized GPSEngine getInstance() {
        if (gpsEngine == null) {
            // Thread-1 释放CPU执行器   Thread-0来执行了 结果 new GPSEngine   此时Thread-1获得执行资格 有 new
            gpsEngine = new GPSEngine();
        }
        return gpsEngine;
    }

    /*public static GPSEngine getGpsEngine() {
        if (null == gpsEngine) {
            synchronized (GPSEngine.class) { // 类锁  还没有new GPSEngine  == 没有this
                if (null == gpsEngine) {
                    gpsEngine = new GPSEngine();
                }
            }
        }
        return gpsEngine;
    }*/
}

②对象锁
区别于类锁,对象锁就是用一个对象类作为锁。

/**
 * synchronized 内置
 *
 * 类说明:synchronized的作用
 *
 * 对象锁
 *
 */
public class SynTest {

    private long count =0;
    private Object obj = new Object(); // 作为一个锁 对象锁obj
    private String str = new String(); // 作为一个锁 对象锁str

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

    // count进行累加
    public void incCount(){

        // T1 T2
        synchronized (obj){ // 使用一把锁
            // T0 T1 T2 == 多个线程 并发问题
            count++;
        }
    }

    // count进行累加
    // synchronized == 对象锁 == this
    public synchronized void incCount2(){
            count++;
    }

    // count进行累加
    // this == 对象锁
    public void incCount3(){
        synchronized (this){
            count++;
        }
    }

    // 线程
    private static class Count extends Thread{

        private SynTest simplOper;

        public Count(SynTest simplOper) {
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for(int i=0;i<10000;i++){
                simplOper.incCount(); // count = count+10000
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynTest simplOper = new SynTest();

        // 启动两个线程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();

        Thread.sleep(50);

        // 2w

        System.out.println(simplOper.count);//20000
    }
}

③显示锁
显示锁,其实就是自定义锁,就是说锁的行为可以由我们自己来自由控制。
而上面的类锁和对象锁,都是由synchronize来帮我们实现的,我们不需要管太多。

/**
 * 使用显示锁的范式
 */
public class LockDemo {

    private int count = 0;

    // 内置锁 == this
    private synchronized  void test() {

    }

    // 内置锁 == LockDemo.class
    private static synchronized  void test2() {

    }

    private synchronized void test3() {
        // 业务逻辑,无法被中断
    }


    // 声明一个显示锁之可重入锁  new 可重入锁
    // 非公平锁
    private Lock lock = new ReentrantLock();
    
    public void incr(){
        // 使用 显示锁 的规范
        lock.lock();
        try{
            count++;
        } finally {   // 打死都要执行  最后一定会执行
            lock.unlock();
        }
    }

    // 可重入锁🔒  意思就是递归调用自己,锁可以释放出来
    // synchronized == 天生就是 可重入锁🔒
    // 如果是非重入锁🔒 ,就会自己把自己锁死
    public synchronized void incr2(){
        count++;
        incr2();
    }

    public static void main(String[] args) {
        LockDemo lockDemo = new LockDemo();
    }

}
死锁是怎么造成的?

死锁就是程序被彻底锁住了,导致程序无法继续执行。
一般死锁的原因就是synchronize嵌套使用了,避免这种情况,基本就避免了死锁。

线程安全的应用场景

生产者消费者模式,就是很经典的例子。
而其中音视频的处理,就是其中之一,因为音频和视频我们需要达到同步。

相关文章

网友评论

      本文标题:JAVA-线程安全

      本文链接:https://www.haomeiwen.com/subject/jbhbqltx.html