欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

徹底搞懂Java多線程(二)

 更新時(shí)間:2021年07月03日 16:45:35   作者:保護(hù)眼睛  
這篇文章主要給大家介紹了關(guān)于Java面試題之多線程和高并發(fā)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

Java中的鎖

Java中的加鎖操作有兩種:

1.synchronized鎖(jvm層的解決方案,也叫監(jiān)視器鎖)

在操作系統(tǒng)的層面使用的是互斥鎖(mutex lock)

在Java中放在了對象頭中。

2.手動鎖Lock

操作鎖的流程

  • 1.嘗試獲取鎖
  • 2.使用鎖
  • 3.釋放鎖

synchronized鎖

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 14:12;
 */
class Counter2 {
    private static volatile int count = 0;
    public void increase() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }
    public void decrease() {
        for (int i = 0; i < 10000; i++) {
            count--;
        }
    }
    public int getCount() {
        return count;
    }
}

public class ThreadDemo19 {
    public static void main(String[] args) throws InterruptedException {
        //聲明鎖對象,任何的對象都可以作為鎖
        Object lock = new Object();
        Counter2 counter2 = new Counter2();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //使用鎖
                synchronized (lock) {
                    counter2.decrease();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                counter2.increase();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(counter2.getCount());
    }
}

結(jié)果是:

在這里插入圖片描述

synchronized使用場景

1.使用synchronized來修飾代碼塊(可以給任意的對象進(jìn)行加鎖操作)

public class ThreadDemo19 {
    public static void main(String[] args) throws InterruptedException {
        //聲明鎖對象,任何的對象都可以作為鎖
        Object lock = new Object();
        Counter2 counter2 = new Counter2();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //使用鎖
                synchronized (lock) {
                    counter2.decrease();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                counter2.increase();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(counter2.getCount());
    }
}

在這里插入圖片描述

2.使用synchronized來修飾靜態(tài)方法(對當(dāng)前的類進(jìn)行加鎖的操作)

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 14:02;
 */
class Counter1 {
    private static volatile int count = 0;
    public void increase() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }
    public void decrease() {
        for (int i = 0; i < 10000; i++) {
            count--;
        }
    }
    public int getCount() {
        return count;
    }
}

public class ThreadDemo18 {
    public static void main(String[] args) throws InterruptedException {
        Counter1 counter1 = new Counter1();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                counter1.decrease();
            }
        });
        Thread thread2 = new Thread(() -> {
            counter1.increase();
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(counter1.getCount());
    }
}

在這里插入圖片描述

3.使用synchronized來修飾普通的方法(對當(dāng)前類的實(shí)例來進(jìn)行加鎖)

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 14:12;
 */
public class ThreadDemo20 {
    private static int num = 0;
    private static final int maxSize = 100000;
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo20 threadDemo20 = new ThreadDemo20();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                threadDemo20.increase();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
               threadDemo20. decrease();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(num);
    }
    //給靜態(tài)的方法進(jìn)行加鎖,被加的鎖是當(dāng)前的對象。
//    public synchronized static void increase(){
    //給普通的方法進(jìn)行加鎖的操作
    public synchronized void increase() {
        for (int i = 0; i < maxSize; i++) {
            num++;
        }
    }
    //    public synchronized static void decrease(){
    public synchronized void decrease() {
        for (int i = 0; i < maxSize; i++) {
            num--;
        }
    }
}

在這里插入圖片描述

synchronized注意事項(xiàng)

1.加鎖的時(shí)候一定要使用同一把鎖對象

Lock類的使用

也叫手動鎖

package ThreadDeom;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 18:32;
 */
public class ThreadDemo22 {
    private static int number = 0;
    private static final int maxSize = 100000;
    public static void main(String[] args) {
        //創(chuàng)建lock鎖對象,lock是接口,不能實(shí)列化
        Lock lock = new ReentrantLock();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < maxSize; i++) {
                lock.lock();
                try {
                    number++;
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < maxSize; i++) {
                lock.lock();
                try {
                    number--;
                } finally {
                    lock.unlock();
                }
            }
        });
        System.out.println(number);
    }
}

在這里插入圖片描述

Lock鎖使用的注意事項(xiàng)

lock()操作一定要放在try外面

如果放在try的里面:

1.try中拋出了異常,還沒有加鎖就釋放了finally中的鎖的操作了

2.如果放在了try,沒加鎖就釋放了鎖,就會拋出異常,就會將業(yè)務(wù)代碼中的異常吞噬掉👇如果一定要放的話,將lock()放在try的第一行。

package ThreadDeom;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 18:49;
 */
public class ThreadDemo23 {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        try{
            System.out.println(1/0);
            lock.lock();
        } finally {
            lock.unlock();
        }
    }
}

在這里插入圖片描述

公平鎖、非公平鎖

公平鎖的調(diào)度:

一個(gè)線程釋放鎖。

主動喚醒“需要得到鎖”的隊(duì)列來得到鎖。

非公平鎖

當(dāng)一個(gè)線程釋放鎖之后,另一個(gè)線程剛好執(zhí)行到獲取鎖的代碼就可以直接獲取鎖。

Java中的所有鎖默認(rèn)都是非公平鎖。

非公平鎖的性能更高。

ReentrantLock可以設(shè)置非公平鎖。

公平鎖

package ThreadDeom;
import java.util.concurrent.locks.ReentrantLock;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 19:22;
 */
public class ThreadDemo24 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                reentrantLock.lock();
                try {
                    System.out.println("thread1");
                } finally {
                    reentrantLock.unlock();
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                reentrantLock.lock();
                try {
                    System.out.println("thread2");
                } finally {
                    reentrantLock.unlock();
                }
            }
        });
        Thread.sleep(100);
        thread1.start();
        thread2.start();
    }
}

打印的結(jié)果是無序的

在這里插入圖片描述

如果設(shè)置為公平鎖:👇

在這里插入圖片描述

在這里插入圖片描述

thread1和thread2 交替輸出

synchronzied 和 Lock 的區(qū)別

1.synchronzied可以自動的進(jìn)行加鎖和釋放鎖,而Lock需要手動的加鎖、釋放鎖。

2.Lock是Java層面的鎖實(shí)現(xiàn),而synchronzied 是JVM層面鎖的實(shí)現(xiàn)

3.synchronzed 即可以修飾代碼塊,又可以修飾普通方法和靜態(tài)的方法,而Lock 只能修飾代碼塊

4.synchronized 實(shí)現(xiàn)的是 非公平的鎖,而Lock 可以實(shí)現(xiàn)公平鎖。

5.lock的靈活性更高

死鎖

在兩個(gè)或兩個(gè)以上的線程運(yùn)行中,因?yàn)橘Y源的搶占而造成線程一直等待的問題???#128071;:

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 19:48;
 */
public class ThreadDemo25 {
    public static void main(String[] args) throws InterruptedException {
        Object lockA = new Object();
        Object lockB = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + "獲取到lockA");
                //讓線程2獲取lockB
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "獲取到lockB");
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //線程2獲取資源B
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "獲取到lockB");
                    //讓線程1先獲取到鎖lockA
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lockA) {
                        System.out.println(Thread.currentThread().getName() + "獲取到lockA");
                    }
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

這就造成了死鎖

在這里插入圖片描述

造成死鎖的四個(gè)條件

1.互斥條件:

當(dāng)資源被一個(gè)線程擁有之后,就不能被其它的線程擁有了

2.擁有請求條件:

當(dāng)一個(gè)線程擁有了一個(gè)資源之后,又試圖請求另一個(gè)資源。

3.不可剝奪條件:

當(dāng)一個(gè)線程擁有了一個(gè)資源之后,如果不是這個(gè)線程主動的釋放資源,其他線程就不能擁有這個(gè)線程。

4.環(huán)路等待條件:

兩個(gè)或兩個(gè)以上的線程擁有了資源之后,試圖獲取對方的資源的時(shí)候形成了一個(gè)環(huán)路。

死鎖的解決方案

解決請求擁有和環(huán)路等待。

最有效的解決方案就是控制加鎖的順序。

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 20:25;
 */
public class ThreadDemo26 {
    public static void main(String[] args) throws InterruptedException {
        Object lockA = new Object();
        Object lockB = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + "獲取到lockA");
                //讓線程2獲取lockB
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "獲取到lockB");
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lockA) {
                    System.out.println(Thread.currentThread().getName() + "獲取到lockA");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lockB) {
                        System.out.println(Thread.currentThread().getName() + "獲取到lockB");
                    }
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

在這里插入圖片描述

線程間通信

線程之間的通訊是指在一個(gè)線程中的操作可以影響另一個(gè)線程。

wait/notify機(jī)制的原理

擁有相同鎖的線程之間才能使用wait/notify機(jī)制。

wait()是Object()的方法,它的作用是是當(dāng)前執(zhí)行wait()方法的線程等待,在wati()所在的代碼出停止執(zhí)行,并釋放鎖,直到接到通知或者被中斷為止。即在調(diào)用wait()的方法之前,線程必需先獲取到對象級別的鎖,也就是只能在同步方法或者同步塊中使用wait()方法。

如果在使用wait()方法之前線程沒有獲得相應(yīng)的鎖,那么程序在執(zhí)行時(shí)就會拋出異常。

notify()方法要在同步方法或者同步塊中執(zhí)行,即在調(diào)用notify()方法之前,線程必需要先獲取到鎖對象。如果線程沒有持有鎖對象的話,那么也會拋出異常。該方法用來通知可能在等待該鎖的其它線程,如果有多個(gè)線程,那么則按照執(zhí)行wait()方法的順序來對處于wait()方法的線程發(fā)出通知,并使該線程重新獲取鎖。執(zhí)行notify()方法之后,當(dāng)前線程不會馬上釋放鎖,處于wait()狀態(tài)的線程也不會立馬得到這個(gè)對象鎖。而是要等notify的synchronized同步區(qū)域執(zhí)行完成之后才會釋放鎖,處于wait()狀態(tài)的線程才會得到鎖對象。

總結(jié):wait()方法用于讓線程停止運(yùn)行,而notify()方法用于通知暫停的線程繼續(xù)運(yùn)行。

在使用wait()或者notify()方法之前沒有對象鎖,就會報(bào)異常👇:

        lock.notify();

在這里插入圖片描述

正確的使用之后

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-12;
 * time: 21:11;
 */
public class ThreadDemo27 {
    //設(shè)置鎖對象
    private static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("在wait()");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("被notify()喚醒之后");
                }
            }
        });
        thread.start();
        Thread.sleep(1000);
        synchronized (lock) {
            lock.notify();
        }
    }
}

在這里插入圖片描述

注意:使用wait()方法的時(shí)候一定要和線程的鎖對象是一個(gè)鎖。

notifyAll

在多線程的情況下使用notify()方法只可以喚醒一個(gè)線程👇

在這里插入圖片描述

package ThreadDeom;
/**
 * user:ypc;
 * date:2021-06-13;
 * time: 8:06;
 */
public class ThreadDemo28 {
    //設(shè)置鎖對象
    private static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("thread1在wait()");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread1被notify()喚醒之后");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("thread2在wait()");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread2被notify()喚醒之后");
            }
        });
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    System.out.println("thread3在wait()");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread3被notify()喚醒之后");
                }
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        Thread.sleep(1000);
        synchronized (lock) {
            System.out.println("主線程調(diào)用notify()之后");
            lock.notify();
        }
    }
}

那么如果使用notifyAll()方法呢?

在這里插入圖片描述

可以看到所有的線程都被喚醒了

在這里插入圖片描述

那么使用notify()喚醒的線程有沒有什么順序呢?

使用notify()喚醒線程的順序是正序、倒序、還是隨機(jī)的,這取決與JVM的具體實(shí)現(xiàn),并不是所有的JVM在執(zhí)行notify()時(shí)都是按照wait()的執(zhí)行順序進(jìn)行喚醒的,也不是所有的notidyAll()都是按照wait()方法的倒序進(jìn)行喚醒的,這取決于JVM的具體實(shí)現(xiàn)。

wait()和notify()不能喚醒指定的線程。

wait()和sleep()的區(qū)別

也可以讓wait()等待指定的時(shí)間,如果超過給定的時(shí)間,wait()不會無限期的等待下去.

在這里插入圖片描述

沒有被notify()喚醒,過了1000毫秒之后會自動停止。

在這里插入圖片描述

wait()在不傳入任何參數(shù)的時(shí)候,線程會進(jìn)入waiting 的狀態(tài),而在wait()中加入一個(gè)大于0的參數(shù)的時(shí)候,線程會進(jìn)入time_wating的狀態(tài)。

sleep()和wait()的區(qū)別 : 線程在sleep()的時(shí)候是不會釋放鎖的,而執(zhí)行wait()的時(shí)候它就會釋放鎖。👇:

package ThreadDeom;
import jdk.nashorn.internal.ir.Block;
/**
 * user:ypc;
 * date:2021-06-13;
 * time: 8:45;
 */
public class ThreadDemo29 {
    private static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    try {
                        System.out.println("thread獲取到了鎖");
                        //如果sleep釋放鎖的話,會在thread獲取到了鎖和thread釋放了鎖之間打印
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
                System.out.println("thread釋放了鎖");
            }
        });
        thread.start();
        //讓thread 先獲取到鎖
        Thread.sleep(1000);
        synchronized (lock) {
            System.out.println("主線程獲取到了鎖");
        }
    }
}

在這里插入圖片描述

可以看到線程在sleep()的時(shí)候,線程是不會釋放鎖的。再來看看wait()方法👇:

在這里插入圖片描述

在這里插入圖片描述

線程使用wait()的時(shí)候它就會釋放掉鎖。

1.wait()和sleep()都是讓線程進(jìn)行休眠的

2.wait()和sleep()方法都有可能在執(zhí)行的過程接收到線程終止的通知

3.wait()必須和synchronzied一起使用,而sleep()不用。

4.wait()會釋放鎖,而sleep()不會釋放鎖。

5.wait()時(shí)Object的方法,而sleep()時(shí)Thread的方法。

6.默認(rèn)情況下,wait()不傳任何的參數(shù)的情況下,wait()會進(jìn)入waiting的狀態(tài),如果傳遞了參數(shù),wait()會進(jìn)入time_waiting的狀態(tài)。而sleep()進(jìn)入的是time_waiting的狀態(tài)。

sleep(0) 和wait(0)的區(qū)別:

1.sleep(0)表示0毫秒之后繼續(xù)執(zhí)行,而wait(0)表示線程會一直休眠下去wait(0)和wait()是一樣的,wait()的源碼就是調(diào)用了wait(0)方法。

2.sleep(0)表示重新出發(fā)一次CPU的競爭。

為什么wait()會釋放鎖,而sleep()不會釋放鎖?

sleep()需要傳遞一個(gè)最大的等待時(shí)間,也就是說sleep()是可控的,而wait()是不可以傳遞參數(shù)的,從設(shè)計(jì)的層面來說,如果讓wait()一直持有所得話,那么線程就可能一直阻塞。

為什么wait()是Object的方法,而sleep()是線程的方法?

wait()需要操作鎖,而鎖是屬于對象級別的,所有的鎖都是放在對象頭中的,它不是線程級別的,一個(gè)線程可以有多把的鎖,為了靈活,就將wait()放在Object中了。

LockSupport park()/unpark()

使用LockSupport可以解決wait()/notify()隨機(jī)喚醒的問題。

package ThreadDeom;
import java.util.concurrent.locks.LockSupport;
/**
 * user:ypc;
 * date:2021-06-13;
 * time: 9:36;
 */
public class ThreadDemo30 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //讓線程休眠
                LockSupport.park();
                System.out.println("unPark()了thread1");
            }
        });
        Thread thread2 = new Thread(() -> {
            LockSupport.park();
            System.out.println("unPark()了thread2");
        });

        Thread thread3 = new Thread() {
            @Override
            public void run() {
                LockSupport.park();
                System.out.println("unPark()了thread3");
            }
        };

        thread1.start();
        thread2.start();
        thread3.start();

        LockSupport.unpark(thread1);
        LockSupport.unpark(thread2);

    }
}

在這里插入圖片描述

總結(jié)

本篇文章就到這里了,希望可以幫助到你,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • SpringBoot結(jié)合Swagger2自動生成api文檔的方法

    SpringBoot結(jié)合Swagger2自動生成api文檔的方法

    這篇文章主要介紹了SpringBoot結(jié)合Swagger2自動生成api文檔的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-05-05
  • hibernate 三種狀態(tài)的轉(zhuǎn)換

    hibernate 三種狀態(tài)的轉(zhuǎn)換

    本文主要介紹了hibernate三種狀態(tài)的轉(zhuǎn)換。具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-03-03
  • 一看就懂 詳解JAVA泛型通配符T,E,K,V區(qū)別

    一看就懂 詳解JAVA泛型通配符T,E,K,V區(qū)別

    泛型從字面上理解,是指一個(gè)類、接口或方法支持多種類型,使之廣泛化、一般化和更加通用。通配符只有在修飾一個(gè)變量時(shí)會用到,使用它可方便地引用包含了多種類型的泛型;下面我們來深入了解一下吧
    2019-06-06
  • 解決Maven靜態(tài)資源過濾問題

    解決Maven靜態(tài)資源過濾問題

    在我們使用Maven構(gòu)建項(xiàng)目的時(shí)候,會默認(rèn)過濾掉靜態(tài)資源,所以,需要手動來配置,本文就介紹一下Maven靜態(tài)資源過濾的問題解決,感興趣的可以了解一下
    2021-06-06
  • Linux(centos7)安裝jdk1.8的詳細(xì)步驟

    Linux(centos7)安裝jdk1.8的詳細(xì)步驟

    Linux的使用相信大家都要用到j(luò)ava吧,在使用java前我們得先安裝jdk以及配置環(huán)境變量等工作,下面這篇文章主要給大家介紹了關(guān)于Linux(centos7)安裝jdk1.8的詳細(xì)步驟,需要的朋友可以參考下
    2023-10-10
  • SpringBoot JSON全局日期格式轉(zhuǎn)換器實(shí)現(xiàn)方式

    SpringBoot JSON全局日期格式轉(zhuǎn)換器實(shí)現(xiàn)方式

    這篇文章主要介紹了SpringBoot JSON全局日期格式轉(zhuǎn)換器,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • MyBatis中map的應(yīng)用與模糊查詢實(shí)現(xiàn)代碼

    MyBatis中map的應(yīng)用與模糊查詢實(shí)現(xiàn)代碼

    這篇文章主要介紹了MyBatis中map的應(yīng)用與模糊查詢實(shí)現(xiàn)代碼,文中通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11
  • JSON,AJAX,Maven入門基礎(chǔ)

    JSON,AJAX,Maven入門基礎(chǔ)

    這篇文章主要介紹了JSON,AJAX和Maven基礎(chǔ),如何使用AJAX讀取Json數(shù)組里面的數(shù)據(jù),感興趣的小伙伴們可以參考一下,希望能夠幫助到你
    2021-07-07
  • java8新特性將List中按指定屬性排序過濾重復(fù)數(shù)據(jù)的方法

    java8新特性將List中按指定屬性排序過濾重復(fù)數(shù)據(jù)的方法

    這篇文章主要介紹了java8新特性將List中按指定屬性排序過濾重復(fù)數(shù)據(jù)的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件

    Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03

最新評論