Java,八股,cv,算法——双非研0四修之路day15

目录

 昨日总结

今日计划

算法

循环链表

有效的字母异位词

缓存

缓存的作用

缓存的成本

缓存更新策略

Vector

 同步锁

昨日八股联想答案 

今日八股联想

总结


 昨日总结

  • 了解Vector (同步锁),了解缓存(未完结),实现黑马点评短信登录、商铺缓存与数据库的双写一致

  • 找文献
  • 背诵小林coding--Java并发面试篇(1/6)
  • 代码随想录——环形链表|| 、 有效的字母异位词

今日计划

  • 继续了解缓存问题,跟进点评项目
  • 找文献,复现代码
  • 背诵小林coding--Java并发面试篇(1/3)
  • 代码随想录——两个数组的交集,两数之和

算法

循环链表

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 :

输入:head = [3,2,0,-4], pos = 1输出:返回索引为 1 的链表节点解释:链表中有一个环,其尾部连接到第二个节点。

有点偏向数学找规律

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
 
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while(true) {
            if(fast == null || fast.next == null) 
            return null;
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) 
            break;
        }
        fast = head;
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的 字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"输出: true

示例 2:

输入: s = "rat", t = "car"输出: false

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s.length() != t.length())
            return false;
        int [] co = new int[26];
        for(char c : s.toCharArray()) {
            co[c - 'a']++;
        }
        for(char c : t.toCharArray()) {
            co[c - 'a']--;
            if(co[c - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }
}

缓存

就是数据交换的缓冲区(称作Cache),是存储数据的临时地方,一般读写性能较高。

缓存的作用

降低后端负载
提高读写效率,降低响应时间

缓存的成本

数据一致性成本(数据库和在内存缓存的数据可能会发生不一致的情况)
代码维护成本
运维成本 (缓存集群的部署等 )

缓存更新策略

  • 内存淘汰
不用自己维护,利用Redis的内存淘汰机制,当内存不足时自动淘汰部分数据。下次查询时更新缓存。一致性差,维护成本无。
  • 超时剔除
给缓存数据添加TTL时间,到期后自动删除缓存。下次查询时更新缓存。一致性一般,维护成本低 。
  • 主动更新
编写业务逻辑,在修改数据库的同时,更新缓存。一致性好,维护成本高。
三者的选择根据业务场景。当低一致性需求时,选择内存淘汰机制。当高一致性需求时,使用主动更新,并以超时剔除作为兜底方案
  • 对于缓存的操作,应更新数据库时让缓存失效,查询时再更新缓存。而不是每次更新数据库都更新缓存,这样效率太低
  • 要保证缓存与数据库的操作同时发生,采用事务的方式
  • 对于数据库与缓存谁先操作的问题,应先操作数据库在删除缓存。因为在查询缓存未命中后,他需要查询数据库;这是如果另一个线程来操作数据库,更新的操作要比查询数据库的操作提前的情况概率 要小很多。 如果先删除缓存在操作数据库,一旦缓存删除,这是查询的情况很多,查询的速度一定是快于更新数据库的。所以这种情况下发生数据不一致的概率更高 。

 

Vector

  • 是java中的一个经典的集合类,是一个动态的数组,属于矢量队列,继承与AbstractList,实现了List
  • 它是线程安全的,所有的方法都通过synchronized关键字修饰,确保多线程环境下的线程安全。
  • Vector的线程安全机制简单,无须手动同步,在多线程环境下无须额外加锁。但是每次方法调用都需要获取锁,高并发下性能较低,同时也会阻塞其他进程,在现代Java开发中,推荐使用更高效的线程安全集合(CopyOnWriteArrayList等)。
  • 对于扩容机制,默认初始容量为10, 当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数 >0,则将容量的值增加 容量增加系数;否则,将容量大小增加一倍。

 同步锁

是用来解决多线程安全问题的(也叫高并发,当多个线程同时修改一个资源时,就会出现线程安全的问题)

同步锁是一种阻塞式的锁机制。当一个线程获取到同步锁后,其他试图获取该锁的线程会被阻塞,直到持有锁的线程释放锁。

package com.hmdp;

public class Demo {
    static Integer num=10;
    public static void main(String[] args) {
        final DemoSyn syn=new DemoSyn();
        Thread t1=new Thread(){
            public void run(){
                syn.printNum(0);
            }
        };
        Thread t2=new Thread(){
            public void run(){
                syn.printNum(1);
            }
        };
        t1.start();
        t2.start();
    }
    public  void printNum(int i){
        //synchronized(this){
            if(i==0){
                num=100;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }else{
                num=200;
            }
            System.out.println(num);
       // }
    }
}

当注释掉synchronized(this){}时,输出的结果会是100,100或者 200,200.因为没有用到同步锁对两个线程进程进行加锁处理

昨日八股联想答案 

  • Java里的多线程和操作系统里的多线程一样吗,要注意哪些问题

Java里的多线程是基于操作系统的多线程机制实现的,二者本质上都是为了提高系统的并发处理能力和资源利用能力。当Java程序创建一个新线程时,JVM会请求操作系统创建一个对应的内核线程,由操作系统负责该内核线程的调度、上下文切换等操作。

要注意线程安全问题,保证数据的一致性

  • 线程的创建方式
  1. 直接继承Thread类,在该类中重写run方法(线程执行的代码),之后在主函数中创建线程对象,来调用start()方法启动线程。
  2. 实现Runnable接口,如果当一个类继承了其他的类,就不可以在直接继承Thread类了。因此可以继承Runnable接口,同上重写run方法,不同的是将此Runnable对象作为参数传递给Thread类的构造器
  3. 使用线程池
  • Java的线程状态有哪些

new(新建状态):线程被创建但尚未启动

RUNNABLE(可运行状态):线程已经调用了start()方法,线程正在JVM中执行。

BLOCKED(阻塞状态):线程被阻塞

WAITING(等待状态):线程处于无限期等待状态,直到其他线程显式地唤醒它

TIMED_WAITING(计时等待状态:线程处于有限时间的等待状态。

TERMINATED(终止):线程执行完毕

今日八股联想

  • 不同的线程之间如何通信?

  • 线程间通信方式有哪些?

  • 如何停止一个线程?

总结

继续开始学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值