<?xml version="1.0" encoding="utf-8" ?><rss version="2.0"><channel><title><![CDATA[wlm123666的博客]]></title><description><![CDATA[]]></description><link>https://blog.youkuaiyun.com/wlm123666</link><language>zh-cn</language><generator>https://blog.youkuaiyun.com/</generator><copyright><![CDATA[Copyright &copy; wlm123666]]></copyright><item><title><![CDATA[重写equals一定要重写hashcode吗？]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/122275498</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/122275498</guid><author>wlm123666</author><pubDate>Sun, 02 Jan 2022 13:07:15 +0800</pubDate><description><![CDATA[重写equals一定要重写hashcode吗？]]></description><category></category></item><item><title><![CDATA[Java垃圾回收机制(GC)]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119813164</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119813164</guid><author>wlm123666</author><pubDate>Fri, 20 Aug 2021 17:13:07 +0800</pubDate><description><![CDATA[如何识别出当前对象是否是垃圾
垃圾：也就是这个对象以后没人使用（对于这个代码起不到贡献了）
引用计数(在Java中实际上没用到)
就比如有一个对象，创建对象的同时，给这个对象搭配一个计数器；
每次有一个引用指向这个对象，计数器就 +1，每次有一个引用被销毁那么计数器就 -1 ；
当计数器为0的时候，这个对象也就没人引用了，自然也就成了垃圾；
举个例子：
class Test{
 void test(){
	Test t = new Test();
	Test t2 = t;
 }
}

在上述代码中，当执行]]></description><category></category></item><item><title><![CDATA[多线程环境下使用哈希表]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119761524</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119761524</guid><author>wlm123666</author><pubDate>Tue, 17 Aug 2021 18:11:43 +0800</pubDate><description><![CDATA[HashTable
单纯的使用一个synchronized进行加锁~，具体相当于针对整个 HashTable对象，但是坏处就是这样的话锁冲突的概率是非常高的

如果有多个线程，线程1操作的在第一个链表上，线程2操作的元素在别的链表上，这个时候不涉及到线程安全。

此时两个线程，修改不同的变量，实际上是没有线程安全的，但是HashTable直接一把锁，锁住了，线程1去操作的时候，线程2就会被阻塞等待，这样效率就会比较低。
此外，在扩容的时候，如果某个线程T正好触发了扩容，那么这个T就倒霉了，就要负责完成整个扩]]></description><category></category></item><item><title><![CDATA[Java中synchronized的工作原理]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119754993</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119754993</guid><author>wlm123666</author><pubDate>Tue, 17 Aug 2021 15:08:11 +0800</pubDate><description><![CDATA[“锁膨胀” 的过程~
场景：多个线程尝试i++，假设这些线程不是同时运行的。
1）第一个线程开始i++，就需要先加锁~
但是第一个线程首次加锁，不是进行真加锁，而只是在对象头里通过一个特殊标志位，标记一下（某个线程想要获取到锁），这里由于是一个线程进行操作，所以还不涉及到线程问题，所以只是标记一下。这个线程赌后面没有其他线程来竞争这个锁！！这也是一个乐观锁。
同时这不是一个真的加锁，而只是标记了一下，所以我们也叫作偏向锁。
2）当第一个线程以偏向锁的状态进行i++的过程中，第二个线程也来尝试竞争锁~~
这个]]></description><category></category></item><item><title><![CDATA[Java中的一些锁的概念和CAS，以及ABA问题]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119721321</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119721321</guid><author>wlm123666</author><pubDate>Mon, 16 Aug 2021 02:37:21 +0800</pubDate><description><![CDATA[所谓策略，也就是解决一件事有不同的方法！
举个例子：问男生追女生的方式有哪些？

就好比你可以直接下手，去要她的联系方式；
也可以通过她身边的闺蜜下手，一步一步的靠近她；
也或者先去和她搞好关系后，再去表白等等。

乐观锁和悲观锁
乐观锁： 假设认为数据一般情况下不会产生并发冲突，所以在数据进行提交更新的时候，才会正式对数据是否产生并发冲突进行检测，如果发现并发冲突了，则让返回用户错误的信息，让用户决定如
何去做。(认为冲突的概率不是很高，做的工作会更少一些，付出的成本也更低~)
CAS（Compare A]]></description><category></category></item><item><title><![CDATA[Java中的定时器Timer]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119684269</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119684269</guid><author>wlm123666</author><pubDate>Fri, 13 Aug 2021 22:24:47 +0800</pubDate><description><![CDATA[简单来说，定时器就相当于一个“闹钟”，给定时器设定一个任务，约定这个任务在xxx时间之后执行~
Timer类提供了一个核心接口，schedule(安排) 指定一个任务交给定时器，在一定时间之后再去执行这个任务~
如何实现定时器的效果~

Timer中要包含一个Task类，每个Task就表示一个具体的任务实例，Task里面包含一个时间戳（啥时候执行这个任务），还包含一个Runnable实例（用来表示任务具体是啥）。
Timer里面通过一个带优先级的阻塞队列，来组织如干个task。
这里的优先级是按照时间的先后]]></description><category></category></item><item><title><![CDATA[Java多线程中的阻塞队列]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119682399</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119682399</guid><author>wlm123666</author><pubDate>Fri, 13 Aug 2021 20:24:50 +0800</pubDate><description><![CDATA[概述
阻塞队列（BlockingQueue）是一个支持两个附加操作的队列。这两个附加的操作是：在队列为空时，获取元素的线程会等待队列变为非空。当队列满时，存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景。
生产者消费者模型
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不
直接通讯，而通过阻塞队列来进行通讯，所以生产者生产完数据之后不用等待消费者处理，直接扔给阻
塞队列，消费者不找生产者要数据，而是直接从阻塞队列里取，阻塞队列就相当于一个缓冲区，平衡]]></description><category></category></item><item><title><![CDATA[Java中的单例模式]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119651163</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119651163</guid><author>wlm123666</author><pubDate>Thu, 12 Aug 2021 20:27:53 +0800</pubDate><description><![CDATA[单例模式确保某个类只有一个实例，而且自行实例化并向整个系统提供这个实例。
1、单例类只能有一个实例。
　　2、单例类必须自己创建自己的唯一实例。
　　3、单例类必须给所有其他对象提供这一实例。
饿汉式单例
//实例的创建出现在‘类加载’阶段
class SingleTon{
	//创建一个成员，用来保存唯一的一个SingleTon 实例
    private static SingleTon inatance = new SingleTon();
    //提供一个今天的静态的方法，来获取到当前实例
 ]]></description><category></category></item><item><title><![CDATA[Java多线程之对象等待集]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119619913</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119619913</guid><author>wlm123666</author><pubDate>Thu, 12 Aug 2021 16:50:39 +0800</pubDate><description><![CDATA[对象等待集起到的效果就是，协调多个线程之间执行的先后顺序~
举个例子：一群人去ATM取款机里去取款，第一个是张三进去去钱，张三进去后，取完钱出来，发现钱取的不够，于是又进去再取一次，这次再出来的时候，又发现钱取多了，再次进去存一点，这样反反复复，就会导致后面排队的人一直用不到取款机。
由于多线程直接是“抢占式”执行，所以往往某一个线程反复的执行，就会导致后面排队的线程，迟迟没有机会去CPU上执行，就会导致线程饿死~
为了解决这个问题，我们借助对象等待集，合理的协调多个线程之间执行的先后顺序~

wait()]]></description><category></category></item><item><title><![CDATA[Java中volatile的用法]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119618187</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119618187</guid><author>wlm123666</author><pubDate>Wed, 11 Aug 2021 23:19:21 +0800</pubDate><description><![CDATA[volatile
起到的效果是辅助保证线程安全，volatile能够禁止指令重排序，保证内存可见性，但是不保证原子性。主要用于读写同一个变量。
观察如下代码：
public class TestVolatile {
    static class Counter{
        public  int flag = 0;
        public int x = 0;
    }
    public static void main(String[] args) {
        Counter]]></description><category></category></item><item><title><![CDATA[Java中的synchronized用法]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119580473</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119580473</guid><author>wlm123666</author><pubDate>Wed, 11 Aug 2021 01:56:21 +0800</pubDate><description><![CDATA[synchronized
是Java里的一个关键字，起到的一个效果是“监视器锁”~~，它的功能就是保证操作的原子性，同时禁止指令重排序和保证内存的可见性！
public  class TestDemo {
    static  class Counter{
        public int count = 0;

        public void add(){
            count++;
        }
    }
    public static void main(Str]]></description><category></category></item><item><title><![CDATA[Java多线程安全问题(原子性，内存可见性，指令重排序)]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119576776</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119576776</guid><author>wlm123666</author><pubDate>Tue, 10 Aug 2021 21:23:35 +0800</pubDate><description><![CDATA[概述
多线程虽然是更轻量的(相比于多进程来说)来完成了并发编程。但是多个线程是访问同一份内存资源的。并且线程是一个“抢占式”执行的过程（这些线程谁先执行，谁后执行，完全取决于系统的调度器），由于这里的不确定性太多，就可能会导致多个线程在访问同一个资源的时候，出现bug~“线程安全问题”。
举个例子：在一个桌子上放着一盘鸡肉，张三和李四老铁，同时看上了一个鸡大腿，两个人都要去抢这个鸡大腿，这个时候，两人就可能会打起来。
就好比线程1和线程2同时争夺一个资源，就会发生线程不安全问题。
那么这里的“访问” 是啥意]]></description><category></category></item><item><title><![CDATA[Java多线程中常用方法]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119552076</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119552076</guid><author>wlm123666</author><pubDate>Tue, 10 Aug 2021 17:21:26 +0800</pubDate><description><![CDATA[run()方法（只是一个内部方法）
run()方法只是一个类中的普通方法，调用run方法跟调用普通方法一样。
方法 run()称为线程体，它包含了要执行的这个线程的内容，线程就进入了  【运行状态】，开始运行 run 函数当中的代码。
Run 方法运行结束， 此线程终止。再调用start方法报错的。
然后 CPU 再调度其它线。
启动线程start()方法（开启一个线程）
在 Java中启动多线程调用的是start方法。
在start方法中，真正实现多线程的是一个本地的方法start0。
调用start方法]]></description><category></category></item><item><title><![CDATA[K组翻转链表]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119545121</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119545121</guid><author>wlm123666</author><pubDate>Mon, 09 Aug 2021 19:56:58 +0800</pubDate><description><![CDATA[力扣链接
题目描述：
给你一个链表，每 k 个节点一组进行翻转，请你返回翻转后的链表。
k 是一个正整数，它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍，那么请将最后剩余的节点保持原有顺序。

大题思路如下：

翻转局部链表，也就是简单的翻转链表
1.定一个傀儡节点temp，让pre 和 cur执行傀儡节点；
2.让cur先走k步，然后让start指向per.next，end指向cur，cur指向cur.next；
3.翻转局部后，在重新拼接


class Solution {
    pu]]></description><category></category></item><item><title><![CDATA[Java多线程的常见属性(线程状态，守护线程)]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119537220</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119537220</guid><author>wlm123666</author><pubDate>Mon, 09 Aug 2021 19:23:12 +0800</pubDate><description><![CDATA[常见的属性




属性
获取方法




ID
getId()


名称
getName()


状态
getState()


优先级
getPriority()


是否为守护线程
isDaemon()


是否存活
isAlive()


是否被中断
isInterrupted()


ID 是线程的唯一标识，不同线程不会重复
通过getName()来获取到当前线程的名称
public  class TestDemo {
     static Thread t1 = new Thread(){
 ]]></description><category></category></item><item><title><![CDATA[Java实现LRU缓存机制]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119535644</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119535644</guid><author>wlm123666</author><pubDate>Mon, 09 Aug 2021 13:45:12 +0800</pubDate><description><![CDATA[力扣链接

方法一：使用 LinkedHashMap
public class LRUCache{
    int capacity;
    Map&lt;Integer, Integer&gt; map;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new LinkedHashMap&lt;&gt;();
    }

    public int get(int key) {
]]></description><category></category></item><item><title><![CDATA[Java多线程(创建线程的方法)]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119523751</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119523751</guid><author>wlm123666</author><pubDate>Mon, 09 Aug 2021 00:51:31 +0800</pubDate><description><![CDATA[Thread 类是 JVM 用来管理线程的一个类，换句话说，每个线程都有一个唯一的 Thread 对象与之关
联。
创建多线程
通过继承 Thread 类，重写run方法
该方法的好处是 this 代表的就是当前线程，不需要通过Thread.currentThread() 来获取当前线程的引用。
public  class TestDemo extends Thread{
    @Override
    public void run() {
        System.out.println("创建]]></description><category></category></item><item><title><![CDATA[进程和线程]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119518667</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119518667</guid><author>wlm123666</author><pubDate>Sun, 08 Aug 2021 19:04:20 +0800</pubDate><description><![CDATA[冯诺依曼体系结构
首先我们需要先了解一下冯诺依曼体系结构：
我们常见的计算机，如笔记本。我们不常见的计算机，如服务器，大部分都遵守冯诺依曼体系。

计算机大部分都是由一个一个硬件组成的

输入单元：包括键盘，鼠标，扫描仪，写板等。
中央处理器（CPU）：含有运算器和控制器等。
输出单元：显示器，打印机等

同时这里也需要注意：

这里的存储器指的是内存。
不考虑缓存情况，这里的CPU能且只能对内存进行读写，不能访问外设（输入或输出设备）。
外设（输入或输出设备）要输入或者输出数据，也只能写入内存或者从内存中]]></description><category></category></item><item><title><![CDATA[复杂链表的复制]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119486242</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119486242</guid><author>wlm123666</author><pubDate>Sat, 07 Aug 2021 16:00:11 +0800</pubDate><description><![CDATA[力扣链接
牛客链接
复制一个复杂链表。在复杂链表中，每个节点除了有一个 next 指针指向下一个节点，还有一个 random 指针指向链表中的任意节点或者 null。

此题，我们需要用到链表和哈希表的用法
如下图：

1.定义一个cur指向head，遍历链表，每次遍历的时候new一个node节点把当前的节点的值赋给node，但是next域不能赋值，因为如果你要复制一个链表，那么复制出来的链表都会有新的地址，如果你把原来节点的next域，也复制给新的node，就不是深拷贝了，

2.那么我们要怎么解决呢？我]]></description><category></category></item><item><title><![CDATA[Java删除链表中重复的结点]]></title><link>https://blog.youkuaiyun.com/wlm123666/article/details/119485046</link><guid>https://blog.youkuaiyun.com/wlm123666/article/details/119485046</guid><author>wlm123666</author><pubDate>Sat, 07 Aug 2021 13:24:01 +0800</pubDate><description><![CDATA[牛客链接

1.由于链表是排序好的，所以就减少了一些麻烦
首先我们需要定义一个傀儡节点(head)，来减少边界判断，把后续的答案往后的答案接在傀儡节点之后；
让傀儡节点连接，整个链表
同时，定义一个cur 指向 傀儡节点的下一个节点
定义一个pre指向当前傀儡节点

此时需要判断当前cur节点是否和他的下一个节点重复，如果不重复，pre指向cur，cur指向cur.next；
注意：判断cur下一个节点的值时，需要先判断cur.next是否为空，不然会导致空指针~~

此时cur的值和cur.next的值相]]></description><category></category></item></channel></rss>