线程与锁

6 篇文章 1 订阅
5 篇文章 0 订阅

基本概念

  • 线程cpu调度的基本单位
  • 进程:程序执行的基本单位 资源分配的基本单位
  • 程序运行过程: cpu-读取指令–pc(存储指令地址),register(计算) 回写–执行下一条指令
  • 并发:CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替
  • 并行:CPU 多核 ,多个线程可以同时执行; 线程池

wait与sleep的区别

1、来自不同的类

wait => Object

sleep => Thread

2、关于锁的释放

wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!

3、使用的范围是不同的

wait必须在同步代码块中

sleep 可以再任何地方睡

4、是否需要捕获异常

wait 可以不需要捕获异常

sleep 必须要捕获异常

Synchronized 和 Lock 区别

  1. Synchronized 内置的Java关键字, Lock 是一个Java类

  2. Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁

  3. Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁

  4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下

    去;

  5. Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以

    自己设置);

  6. Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!

并发编程的三个特性

  • 可见性
  • 原子性
  • 有序性

阻塞队列 BlockingQueue

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addoffer()putoffer(-,时间,单位)
移除removepoll()takepoll(时间,单位)
检查队首元素elementpeek--

线程池

两种方式:

  • 使用Executors(自动的线程池,不推荐)

    package com.kuang.pool; 
    import java.util.concurrent.ExecutorService; 
    import java.util.concurrent.Executors; // Executors 工具类、3大方法 
    
    public class Demo01 { 
        public static void main(String[] args) { 
            ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线 程
            // ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一 个固定的线程池的大小 
            // ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩 的,遇强则强,遇弱则弱 
            try {for (int i = 0; i < 100; i++) { // 使用了线程池之后,使用线程池来创建线程 
                threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+" ok"); 
                                       }); 
            } 
                } catch (Exception e) { 
                e.printStackTrace(); 
            } finally { 
                // 线程池用完,程序结束,关闭线程池 
                threadPool.shutdown(); 
            } 
        } 
    }
    
  • 手动创建线程,使用ThreadPoolExecutor

    package com.kuang.pool; 
    
    import java.util.concurrent.*; // Executors 工具类、3大方法 
    /*** new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异 常
    * new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里! 
    * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常! 
    * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和等待队列中最早的竞争,也不会 抛出异常! 
    */ 
    
    public class Demo01 { 
        public static void main(String[] args) { // 自定义线程池!工作 ThreadPoolExecutor 
            ExecutorService threadPool = new ThreadPoolExecutor( 
            2, //核心线程池大小
            5,// 最大核心线程池大小
            3, // 超时了没有人调用就会释放
            TimeUnit.SECONDS,  // 超时单位
            new LinkedBlockingDeque<>(3), // 阻塞队列
            Executors.defaultThreadFactory(), // 线程工厂:创建线程的,一般 不用动
            new ThreadPoolExecutor.DiscardOldestPolicy()); //拒绝策略 队列满了,尝试去和 最早的竞争,也不会抛出异常! 
        try {
            // 最大承载:Deque + max // 超过 RejectedExecutionException 
            for (int i = 1; i <= 9; i++) { 
                // 使用了线程池之后,使用线程池来创建线程 
                threadPool.execute(()->{ 
                    System.out.println(Thread.currentThread().getName()+" ok"); 
                });
    } 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { // 线程池用完,程序结束,关闭线程池 
            threadPool.shutdown(); 
        } 
    } 
                        }
    

几种锁

cas: CAS(Compare and swap)

即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。它的实现很简单,就是用一个旧的预期的值和内存值进行比较,如果两个值相等,就用新的值替换内存值,并返回 true。否则,返回 false。

java.util.concurrent下使用了很多的cas,如:AtomicBoolean
cas的领个常见问题:

  • ABA问题:可以使用版本号的方式解决。
  • 原子性问题:只能保证一个共享变量的原子操作。
    会调用c语言,利用了 CPU 的 cmpxchg 指令完成比较并替换,在地层中调用汇编语言的lock锁。

可重入锁

可重入锁

package com.kuang.lock; 
import javax.sound.midi.Soundbank; 
// Synchronized 
public class Demo01 { 
    public static void main(String[] args) { 
        Phone phone = new Phone(); 
        new Thread(()->{ 
            phone.sms(); 
        },"A").start(); 
        new Thread(()->{ phone.sms(); 
                       },"B").start(); 
    } 
}
class Phone{ 
    public synchronized void sms(){ 
        System.out.println(Thread.currentThread().getName() + "sms");
        call(); // 这里也有锁 
    }
    public synchronized void call(){ 
        System.out.println(Thread.currentThread().getName() + "call"); 
    } 
}

锁的升级

加锁可以使一段代码在同一时间只有一个线程可以访问,在增加安全性的同时,牺牲掉的是程序的执行性能,所以为了在一定程度上减少获得锁和释放锁带来的性能消耗,在 jdk6 之后便引入了“偏向锁”和“轻量级锁”,所以总共有4种锁状态,级别由低到高依次为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会随着竞争情况逐渐升级。

注意:锁可以升级但不能降级。

优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距如果线程间存在锁竞争,会带来额外的锁撤销的消耗适用于只有一个线程访问同步块场景
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果始终得不到索竞争的线程,使用自旋会消耗CPU追求响应速度,同步块执行速度非常快
重量级锁线程竞争不使用自旋,不会消耗CPU线程阻塞,响应时间缓慢追求吞吐量,同步块执行速度较慢

volatile

volatile的特点

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

懒汉模式双检索需要使用volatile吗?

需要!

如果在执行的过程中有可能会发生指令重排,如果没有使用volatile的话,就有可能会拿到未初始化的完成的(半初始化)的对象。
volatile不允许乱序执行:
两个语句之间存在内存屏障
内存屏障HostPot

双检索是否使用volatile

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值