多线程应用

线程:依赖于进程,是程序执行一个单一的顺序控制流程,程序执行流的最小单位,处理器调度和分派的基本单位。
进程:系统进行资源分配和调用的独立单位。
区别:一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间
同步:调用者必须等到方法调用返回才能进行后续行为。
并发:多个任务交替执行
阻塞:一个线程占用资源,其他需要这个资源的线程必须等待,等待会导致线程挂起。
死锁:指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。多个线程被阻塞
同步锁:保证线程同步互斥,指并发执行的多个线程。java使用synchronized关键字获取一个对象的同步锁

线程实现

继承thread类

public class MyThread extends Thread { 
   @Override   
    public void run() {       
     System.out.println("MyThread.java");    
     }
 }
   Thread thread = new MyThread();        
   thread.start();

实现Runnable接口

public class MyThread2 implements Runnable {    
    @Override    
   public void run() {        
      System.out.println("MyThread2.java ");    
   }
}
  MyThread2 thread2 = new MyThread2();       
  Thread t = new Thread(thread2);        
  t.start();

实现Callable接口

public class MyThread3 implements Callable<String> {   
     @Override    
     public String call() throws Exception {        
        return "MyThread3.java";    
     }
}
 try {            
     ExecutorService threadPool = Executors.newSingleThreadExecutor();
     Future<String> future = threadPool.submit(new MyThread3());            
     System.out.println(future.get());        
 } catch (InterruptedException e) {            
       e.printStackTrace();        
 } catch (ExecutionException e) {           
       e.printStackTrace();       
 }

什么是线程安全和线程不安全?
加锁的就是是线程安全的,不加锁的就是是线程不安全的
线程安全
就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问,直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全
不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

乐观锁:读多写少,每次拿数据的时候认为不会修改,所以不会上锁。在更新的时候会判断在此期间别人有没有区更新这个数据,采取在写是先读出版本号,然后加锁。
悲观锁:写多,遇到并发写的可能性高,每次在读写数据的时候都会上锁。Java中悲观锁就是synchronized,aqs框架下的锁则是先尝试cas乐观锁去获取锁,获取不利,则转换为悲观锁,如retreenlock。
自旋锁:当线程A想要获取一把自旋锁而该锁又被其它线程锁持有时,线程A会在一个循环中自旋以检测锁是不是已经可用了。
注意

  • 自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,这就会浪费CPU时间
  • 持有自旋锁的线程在sleep之前应该释放自旋锁以便其它线程可以获得自旋锁。

自旋锁优缺点
自旋锁尽可能的减少线程阻塞
but 造成cpu的浪费
synchronized同步锁:非null对象当作锁,独占式的悲观锁,属于可重入锁。
synchronized作用范围

  • 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

synchronized核心组件

  • wait set:调用wait方法被阻塞的线程被放在这里
  • contention list:竞争队列
  • entry list:有资格成为候选资源的线程
  • ondeck:只有一个线程正在竞争锁资源
  • owner:获取到锁资源的线程
  • !owner:释放锁的线程

synchronized同步原理:只是java的一个关键字,使用时并没有看到显示的加锁或解锁的过程,所以有必要通过javap命令,查看相应的字节码文件。

特性

  • 原子性一个操作不可中断。即使是多个线程一起执行的时候,一个操作一旦开始就不会被其它线程干扰。
  • 可见性:一个线程修改了某一个共享变量的值,其它线程是否能立即知道这个修改。
  • 有序性:线程一旦加锁,必须等到其他线程将锁释放

reentrantlock与synchronized
reentrantlock通过方法lock()与unlock()来进行加锁与解锁操作,与synchronized被jvm自动解锁机制不同,它需要手动解锁
reentrantlock相比synchronized可中断、公平锁、多个锁

线程池

java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
线程复用;控制最大并发数;管理线程
线程复用:thread的类有start方法,调用start启动线程是java虚拟机会调用该类的run方法(runnable对象的run)。重写tread类,在start方法中添加runnable对象。获取runnable用queue实现。
线程池组成

  • 线程池管理器:用于创建并管理线程池
  • 工作线程:线程池中的线程
  • 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
  • 任务队列:用于存放待处理的任务,提供一种缓存机制

常见线程池

  • newSingleThreadExecutor
    单个线程的线程池,单线程串行执行
  • newFixedThreadExecutor(n)
    固定数量的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,进入等待队列直到前面的任务完成才继续执行
  • newCacheThreadExecutor(推荐使用)
    可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲的线程,当有任务来时,又智能的添加新线程来执行。
  • newScheduleThreadExecutor
    大小无限制的线程池,支持定时和周期性的执行线程

线程池工作过程

  • 线程池创建,任务队列作为参数传进来。
  • 调用execute()方法添加一个任务
    运行的线程数量小于corepoolsize,创建线程运行任务
    大于等于,放入队列
    队列满了,运行的线程数量小于maximumpoolsize,创建非核心线程运行这个任务
    队列满了,运行的数量大于等于,抛出异常
  • 线程完成任务,从队列中取下一个任务
  • 线程闲,线程池会根据当前运行线程数大于corepoolsize,线程停掉

Java阻塞队列

队列没有数据,消费者端的所有线程被自动阻塞,知道有数据进入队列
队列中填满数据,生产者端的所有线程都会被自动阻塞,直到队列有空的位置,线程被自动唤醒

volatile

稍弱的同步机制,确保将变量的更新操作通知到其他的线程。
变量可见性:该变量对所有线程可见,指修改值其他线程可立即获取
禁止重排序:禁止指令重排
比synchronized更轻量级的同步锁:访问volatile变量时不会执行加锁操作,不会使执行线程阻塞
适合场景:一个变量被多个线程共享,线程直接给变量复赋值

在并发环境的线程安全的条件:
对变量的写操作不依赖于当前值
该变量没有包含在具有其他变量的不变式中,不同的volatile变量之间,不能相互依赖

synchronized和reentrantlock的区别

相同点:

  • 对视用来协调线程对共享对象、变量的访问
  • 都是可重入锁,同一线程可以多次获得锁
  • 保证了可见性和互斥性
    不同点:
  • synchronized隐式获得释放锁,reentrantlock显式获得释放锁
  • synchronized不可相应中断,reentrantlock可相应中断可轮回
  • synchronized是jvm级别,reentrantlock是api级别
  • reentrantlock可以实现公平锁
  • reentrantlock通过condition可以绑定多个条件
  • synchronized是同步阻塞,悲观并发,lock是同步非阻塞,乐观并发策略
  • lock是一个接口,synchronized是java关键字,synchronized是内置的语言实现
  • lock可以提高多个线程进行读写操作
  • lock可以知道有没有成功获取锁
  • lock可以让等待锁的线程中断
  • synchronized发生宜昌市释放占有锁,不会导致死锁,lock需要在finally块中释放锁

JVM线程调度(抢占式调度)
优先级分配cpu时间片运行

CAS

比较并交换-乐观锁机制-锁自旋
ABA问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值