多线程

多线程

多线程就是Java中一个程序分成多个线程共同执行,线程是程序的最小运行单位。线程的调用是根据CPU调用来排序的(没有锁,没有礼让的情况下)。
创建方式:

  1. 继承Thread类,调用start方法来实现多线程的调用,虽然run方法实际上也是调用的run方法,但是如果直接调用run方法并不会多运行一个线程。
  2. 实现Runnable接口,调用start方法来多线程,相较于继承类,实现接口可以多实现。
  3. 实现Callable接口,重写call方法来多线程,可以自定义返回值。

线程的一些方法和状态:

  1. yield-线程礼让:让当前运行的线程礼让其它的线程先运行,但是不会礼让低优先级的线程。
  2. join-线程强制执行:当调用jion方法的线程执行完毕之后,再执行其它的线程。
  3. sleep-线程休眠:让线程指定时间过后再运行,不会释放锁。
  4. stopdestory-停止线程:方法被废弃,推荐让线程自己停下来。
  5. setPriority-设置线程优先级:取值为1-10,值越大优先级越高。

线程同步:用来处理并发, 多个线程访问同一个方法时,需要等待前面的线程执行完毕,后面的线程才会执行进去。

使用synchronized声明代码块,或者是在方法上添加。
Lock锁,调用lock对象的lock方法加锁,调用unlock释放锁。
二者比较:

  1. Synchornnized 是内置的Java关键字,Lock是一个关键字;
  2. Lock可以判断锁的状态;
  3. Lock手动的释放锁;
  4. 如果一个线程阻塞,使用Synchornized会一直等待,Lock会尝试使用tryLock方法去尝试获取。
  5. synchornized是公平的,Lock可以自己设置公共或者不公平。
  6. synchornized适合少量的代码同步问题,Lock适合大量的代码同步问题。

关于锁的问题建议直接看https://blog.csdn.net/weixin_44289860/article/details/107589088

锁的一些问题:

  1. 没有锁的方法与有锁的方法的调用无关;
  2. 与锁的休眠时间无关;
  3. synchornized锁的是调用方法的对象,也就是说如果一个对象调用两个方法,先拿到锁(先被调用就先拿到锁)的就先执行。
  4. 不同的对象调用同步方法,不受对方锁的影响。
  5. 加了static标识符的方法锁的是全局唯一的class对象,多个对象也是同一把锁。
  6. 如果同时存在静态同步方法和普通同步方法,这个两个方法的锁的对象是不同的,所以不需要等待对方释放锁。
    死锁:两个或者两个以上线程等待对方释放锁,就会形成死循环,程序一直卡在这。

线程协作:
多个线程共享一个资源,A线程使用完毕通知B线程,B完毕再通知A,依次循环。最典型的就是生产者和消费者模式。
解决方法:
7. 灯管法:都放到缓冲区中,不直接的进行通信协作。
8. 信号灯法。
9. 线程池:将创建好的线程放到池中,用时取,不用时退回。
10. JUC方法:lock相较于synchornized 的notifyAll方法变成了Condition对象的singalAll方法;wait变成了await方法。除去上述的区别外Condition还新增了对线程执行顺序控制的方法,对线程的精准通知唤醒。

juc

juc是java.util.concurrent下面的atomiclocks包的简称。也是多线程的一种。一般用来解决并发编程的问题。

并发与并行:并发:多个线程操作同一个资源,并行:多个线程同时执行。

List、Set、Map在并发下都是不安全的。

解决方法:

  1. list:List<String> lists = new Vector<>();或者List<String> lists = Collections.synchronizedList(new ArrayList<>());再或者List<String> lists = new CopyOnWriteArrayList<>();最好使用第三个,实现原理是在读写分离,在写入时避免覆盖而造成的数据问题;相较于Vector而言它的add方法并不是使用的synchornized锁同步的方法,效率高。
  2. set:Set <String > strings = Collections.synchronizedSet(new HashSet<>());或者Set<String > set = new CopyOnWriteArraySet<>();
  3. map:Map<String,Sting> map = new ConncurrnetHashMap<>();

Callable接口的补充:
常用的辅助类:

  1. CountDownLatch:类似于减法计数器,设定一个总数,每执行一个线程-1;当普通线程数量归零时执行指定的线程。有就是说等待一组线程执行完毕之后才会同时的去执行下面的代码。
  2. CyclicBarrier 类似与一个加法计数器,当线程增加到达指定的值时执行线程。当一组线程执行到某个状态时就可以继续执行,不用管其它的线程。
  3. Semaphore 类似于一个通行证,用来指定最多运行多少个线程, 每当一个线程运行完毕时,正在等待的线程再运行。一般用来限流和多个线程共享资源。

读写锁
如果写入的过程中其它的线程也执行写入,当前写入线程就会变慢,所以使用读写锁:读可以被多个线程同时读,写的时候只能由一个线程写。独占锁:只有一个线程能够使用;共享锁:多个线程共享锁。读锁是共享锁,写锁是独占锁。

阻塞队列

  1. BlockingQueuehttps://blog.csdn.net/weixin_44289860/article/details/107603643

线程池补充
三大方法:

  1. ExecutorService service = Executors.newSingleThreadExecutor();得到一个线程池;
  2. ExecutorService service = Executors.newFixedThreadPool(5);得到固定线程数量的线程池;
  3. ExecutorService service = Executors.newCachedThreadPool();根据实际情况来设定线程池的数量。

自定义线程池:
使用ThreadPoolExecutor来创建线程。

拒绝策略

  1. AbortPolicy:默认的拒绝策略,当线程数量超过最大的承受范围时会抛出异常。
  2. CallerRunsPolicy:当超过线程池的最大承受范围时,由进入时的线程执行。
  3. DiscardPolicy:队列满了会丢掉任务但是不会抛出异常。
  4. DiscardOldestPolicy:队列满了尝试去和第一个调用的线程竞争,也不会去抛出异常。

Fockjoin
分支合并:并行执行任务来提高效率。把大任务划分成小任务来执行,再将小任务合并为最终结果。
工作窃取:不让执行完毕任务的线程等待,去帮助其它的线程执行任务。

异步回调
类似于前端的ajax,再Java中执行异步回调。

JMM
Java内存模型
约定:

  1. 线程解锁前,必须把共享变量立刻刷新回主内存;
  2. 线程加锁前,必须把读取线程主内存中的最新值传到工作内存中;
  3. 必须保证加锁和减锁是同一把锁。

Vloatile
Java虚拟机提供轻量级的同步机制。

  1. 保证可见性:如果工作内存无法及时的获得主内存中已经修改了的值,那么就不会及时的做出决定。
  2. 不保证原子性:
  3. 禁止指令重排:编译器优化会重排代码执行,指令并行也可能会重排,内存系统也会重排。处理器在执行指令重排的时候会考虑数据之间的依赖性。但是在多线程共同执行时,程序就不一定会出现应有的数值。添加volatile可以避免指令重排。原理:内存屏障:保证特定的操作的执行顺序。可以保证某些变量的内存可见性。在单例模式使用的最多。

原子性:不可分割。线程A在执行任务时,不能被打扰,要么同时成功要么同时失败。
如何保证原子性:
lock&synchornized 加锁可以保证原子性,但是消耗的资源比较大。
使用原子类->atomic

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值