JAVA基礎系列(八) 多線程

假如一個程序有多條執行流程,那么,該程序就是多線程程序。

1.多線程概述

1.1 進程與線程

進程:
正在運行的程序,是系統進行資源分配和調用的獨立單位。
每一個進程都有它自己的內存空間和系統資源。
線程:
是進程中的單個順序控制流,是一條執行路徑
一個進程如果只有一條執行路徑,則稱為單線程程序。
一個進程如果有多條執行路徑,則稱為多線程程序。

1.2 線程調度

CPU分配使用權的機制

線程有兩種調度模型:
分時調度模型: 所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間片
搶占式調度模型 : 優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那么會隨機選擇一個,優先級高的線程獲取的 CPU 時間片相對多一些。
Java使用的是搶占式調度模型。

1.3 Java程序運行原理

java 命令會啟動 java 虛擬機,啟動 JVM,等于啟動了一個應用程序,也就是啟動了一個進程。該進程會自動啟動一個 “主線程” ,然后主線程去調用某個類的 main 方法。所以 main方法運行在主線程中。在此之前的所有程序都是單線程的。

思考題:jvm虛擬機的啟動是單線程的還是多線程的?
?????答:多線程的。原因是垃圾回收線程也要先啟動,否則很容易會出現內存溢出?,F在的垃圾回收線程加上前面的主線程,最低啟動了兩個線程,所以,jvm的啟動其實是多線程的。

2.Thread類

public class Thread extends Object implements Runnable
繼承了Object類實現了Runnable接口

2.1 構造方法

public Thread();
public Thread(String name);
public Thread(Runnable target);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target);
...

2.2 成員方法即線程控制

//線程休眠
public static void sleep(long millis)

//線程加入,等待該線程終止。 
public final void join()

//線程禮讓,暫停當前正在執行的線程對象,并執行其他線程。
public static void yield()

//后臺線程,將該線程標記為守護線程或用戶線程。
//當正在運行的線程都是守護線程時,Java 虛擬機退出。 該方法必須在啟動線程前調用
public final void setDaemon(boolean on)

//中斷線程
//讓線程停止,過時了,但是還可以使用。
public final void stop()
中斷線程。 把線程的狀態終止,并拋出一個InterruptedException。
public void interrupt()

// 設置線程優先級,范圍為1-10,默認為5
// 線程優先級高僅僅表示線程獲取的 CPU時間片的幾率高,但是要在次數比較多,或者多次運行的時候才能看到比較好的效果。
public final void setPriority(int newPriority)

注意:
run與Start 的區別:
1.start方法
用 start方法來啟動線程,是真正實現了多線程, 通過調用Thread類的start()方法來啟動一個線程,這時此線程處于就緒(可運行)狀態,并沒有運行,一旦得到cpu時間片,就開始執行run()方法。但要注意的是,此時無需等待run()方法執行完畢,即可繼續執行下面的代碼。所以run()方法并沒有實現多線程。
2.run方法
run()方法只是類的一個普通方法而已,如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是只有一條,還是要順序執行,還是要等待run方法體執行完畢后才可繼續執行下面的代碼。

2.3 Thread.State

枚舉類,表示線程運行周期中的幾種狀態。

**NEW **
狀態是指線程剛創建, 尚未啟動

**RUNNABLE **
狀態是線程正在正常運行中, 當然可能會有某種耗時計算/IO等待的操作/CPU時間片切換等, 這個狀態下發生的等待一般是其他系統資源, 而不是鎖, Sleep等

**BLOCKED **
這個狀態下, 是在多個線程有同步操作的場景, 比如正在等待另一個線程的synchronized 塊的執行釋放, 或者可重入的 synchronized塊里別人調用wait() 方法, 也就是這里是線程在等待進入臨界區

**WAITING **
這個狀態下是指線程擁有了某個鎖之后, 調用了他的wait方法, 等待其他線程/鎖擁有者調用 notify / notifyAll 一遍該線程可以繼續下一步操作, 這里要區分 BLOCKED 和 WATING 的區別, 一個是在臨界點外面等待進入, 一個是在臨界點里面wait等待別人notify, 線程調用了join方法 join了另外的線程的時候, 也會進入WAITING狀態, 等待被他join的線程執行結束

**TIMED_WAITING **
這個狀態就是有限的(時間限制)的WAITING, 一般出現在調用wait(long), join(long)等情況下, 另外一個線程sleep后, 也會進入TIMED_WAITING狀態

**TERMINATED **
這個狀態下表示該線程的run方法已經執行完畢了, 基本上就等于死亡了(當時如果線程被持久持有, 可能不會被回收)

幾種狀態之間的關系圖解如下:


注意:在JDK8的API中 Thread.State 并沒有RUNNING這個狀態,這里是為了便于理解畫在圖中。

3.多線程實現的幾種方式

JAVA多線程實現方式主要有三種:

3.1、繼承Thread類,并重寫run()方法

** Demo:**

public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建三個線程對象
        SellTicket st1 = new SellTicket();
        SellTicket st2 = new SellTicket();
        SellTicket st3 = new SellTicket();

        // 給線程對象起名字
        st1.setName("窗口1");
        st2.setName("窗口2");
        st3.setName("窗口3");

        // 啟動線程
        st1.start();
        st2.start();
        st3.start();
    }
}

class SellTicket extends Thread {

    // 定義100張票
    // 為了讓多個線程對象共享這100張票,我們其實應該用靜態修飾
    private static int tickets = 100;

    @Override
    public void run() {
        // 是為了模擬一直有票
        while (tickets > 0) {
            System.out.println(getName() + "正在出售第" + (tickets--) + "張票");
        }
    }
}

3.2、 實現Runnable接口,實現run()方法

** Demo:**

/*
 * 實現Runnable接口的方式實現
 */
public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;

    @Override
    public void run() {
        while (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "正在出售第"
                    + (tickets--) + "張票");
        }
        System.out.println(Thread.currentThread().getName() + ":沒票了哦!");
    }
}

3.3、使用ExecutorService、Callable、Future實現有返回結果的多線程。

其中前兩種方式線程執行完后都沒有返回值,只有最后一種是帶返回值的。

** Demo:**

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableDemo {
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        FutureTask<Integer> f1 = new FutureTask<Integer>(new MyCallable(100));
        FutureTask<Integer> f2 = new FutureTask<Integer>(new MyCallable(200));

        // FutureTask實現了兩個接口,Runnable和Future
        new Thread(f1).start();
        new Thread(f2).start();

        Integer i1 = f1.get();
        Integer i2 = f2.get();

        System.out.println(i1);
        System.out.println(i2);
    }
}

class MyCallable implements Callable<Integer> {

    private int number;

    public MyCallable(int number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int x = 1; x <= number; x++) {
            sum += x;
        }
        return sum;
    }

}

** Demo:**使用線程池

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableDemo {
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        // 創建線程池對象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以執行Runnable對象或者Callable對象代表的線程
        Future<Integer> f1 = pool.submit(new MyCallable(100));
        Future<Integer> f2 = pool.submit(new MyCallable(200));

        // V get()
        Integer i1 = f1.get();
        Integer i2 = f2.get();

        System.out.println(i1);
        System.out.println(i2);

        // 結束
        pool.shutdown();
    }
}

class MyCallable implements Callable<Integer> {

    private int number;

    public MyCallable(int number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int x = 1; x <= number; x++) {
            sum += x;
        }
        return sum;
    }

}

3.4 匿名內部類方式使用多線程

new Thread(){代碼…}.start();
New Thread(new Runnable(){代碼…}).start();

** Demo:**

/*
 * 匿名內部類的格式:
 *      new 類名或者接口名() {
 *          重寫方法;
 *      };
 *      本質:是該類或者接口的子類對象。
 */
public class ThreadDemo {
    public static void main(String[] args) {
        // 繼承Thread類來實現多線程
        new Thread() {
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":"
                            + x);
                }
            }
        }.start();

        // 實現Runnable接口來實現多線程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println(Thread.currentThread().getName() + ":"
                            + x);
                }
            }
        }) .start();

        // 更有難度的
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("hello" + ":" + x);
                }
            }
        }) {
            public void run() {
                for (int x = 0; x < 100; x++) {
                    System.out.println("world" + ":" + x);
                }
            }
        }.start();
    }
}

4.線程同步

4.1 同步代碼塊

** Demo:**

public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;
    // 創建鎖對象
    private Object obj = new Object();

    @Override
    public void run() {
        while (tickets > 0) {
            synchronized (obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票");
                }
            }

        }
        System.out.println(Thread.currentThread().getName() + ":沒票了哦!");
    }
}

4.2 同步方法

** Demo:**

public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個線程對象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicket implements Runnable {

    // 定義100張票
    private static int tickets = 100;

    private int x = 0;

    @Override
    public void run() {
        while (tickets > 0) {
            if (x % 2 == 0) {
                // 當同步方法為靜態時鎖對象應該為SellTicket.class
                synchronized (this) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "張票 ");
                    }
                }
            } else {
                sellTicket();
            }
            x++;
        }
        System.out.println(Thread.currentThread().getName() + ":沒票了哦!");
    }

    // 當同步方法為靜態時鎖對象應該為SellTicket.class
    private synchronized void sellTicket() {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第"
                    + (tickets--) + "張票 ");
        }
    }
}

** 注意:**
1.同步代碼塊的鎖對象是任意對象
2.同步方法的鎖對象是this。
3.靜態同步方法的鎖對象是類的字節碼文件對象。

4.3 Lock鎖的使用

雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock

Demo:

/*
 * Lock:
 *      void lock(): 獲取鎖。
 *      void unlock():釋放鎖。  
 * ReentrantLock是Lock的實現類.
 */

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicketDemo {
    public static void main(String[] args) {
        // 創建資源對象
        SellTicket st = new SellTicket();

        // 創建三個窗口
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 啟動線程
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicket implements Runnable {

    // 定義票
    private int tickets = 100;

    // 定義鎖對象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (tickets > 0) {
            try {
                // 加鎖
                lock.lock();
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票");
                }
            } finally {
                // 釋放鎖
                lock.unlock();
            }
        }
        System.out.println(Thread.currentThread().getName() + ":沒票了哦!");
    }

}

4.4 死鎖

是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象.
如果出現了同步嵌套,就容易產生死鎖問題

Demo:

/*
 * 同步的弊端:
 *      A:效率低
 *      B:容易產生死鎖
 * 
 * 
 * 舉例:
 *      中國人,美國人吃飯案例。
 *      正常情況:
 *          中國人:筷子兩支
 *          美國人:刀和叉
 *      現在:
 *          中國人:筷子1支,刀一把
 *          美國人:筷子1支,叉一把
 */
public class DieLockDemo {
    public static void main(String[] args) {
        DieLock dl1 = new DieLock(true);
        DieLock dl2 = new DieLock(false);

        dl1.start();
        dl2.start();
    }
}

class DieLock extends Thread {

    private boolean flag;

    public DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (MyLock.objA) {
                System.out.println("if objA");
                synchronized (MyLock.objB) {
                    System.out.println("if objB");
                }
            }
        } else {
            synchronized (MyLock.objB) {
                System.out.println("else objB");
                synchronized (MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}

class MyLock {
    // 創建兩把鎖對象
    public static final Object objA = new Object();
    public static final Object objB = new Object();
}

5. 線程通信

針對同一個資源的操作有不同種類的線程

Demo:生產者消費者模型

import java.util.Vector;

/*
 * 籃子容量為5,生產者每1秒鐘生產一個產品,消費者每2秒鐘消費一個產品
 * 當籃子為空時消費者會等待生產者生產,當籃子裝滿時生產者等消費者消費
 */

public class ProcucerAndConsumerDemo {
    public static void main(String[] args) {
        // 用Vector來模擬籃子
        Vector obj = new Vector();
       // 通過構造函數來共享一個對象
        Thread consumer = new Thread(new Consumer(obj));
        Thread producter = new Thread(new Producer(obj));
        consumer.start();
        producter.start();
    }
}

class Producer implements Runnable {
    private Vector obj;

    public Producer(Vector v) {
        this.obj = v;
    }

    public void run() {
        while (true) {
            synchronized (this.obj) {
                try {
                    if (this.obj.size() > 4) {
                        System.out.println("Producter:the basked has full!");
                        this.obj.wait();
                    }
                    this.obj.add(new String("apples"));
                    System.out.println("Producter:I have produced one");
                    this.obj.notify();
                    Thread.sleep(1000);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class Consumer implements Runnable {
    private Vector obj;

    public Consumer(Vector v) {
        this.obj = v;
    }

    public void run() {
        while (true) {
            synchronized (this.obj) {
                try {
                    if (this.obj.size() == 0) {
                        System.out.println("Consumer:the basked is null!");
                        this.obj.wait();
                    }
                    this.obj.remove(0);
                    System.out.println("Consumer:I have taken one");
                    System.out.println("obj size: " + this.obj.size());
                    this.obj.notify();
                    Thread.sleep(2000);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Result:

Consumer:the basked is null!
Producter:I have produced one
Producter:I have produced one
Consumer:I have taken one
obj size: 1
Consumer:I have taken one
obj size: 0
Consumer:the basked is null!
Producter:I have produced one
Producter:I have produced one
Producter:I have produced one
Producter:I have produced one
Producter:I have produced one
Consumer:I have taken one
obj size: 4
Consumer:I have taken one
obj size: 3
Consumer:I have taken one
obj size: 2
Consumer:I have taken one
obj size: 1
Consumer:I have taken one
obj size: 0
Consumer:the basked is null!
Producter:I have produced one
Producter:I have produced one

6.線程組

把多個線程組合到一起。它可以對一批線程進行分類管理,Java允許程序直接對線程組進行控制。

構造方法

private ThreadGroup();
public ThreadGroup(String name);
public ThreadGroup(ThreadGroup parent, String name);

成員方法

public final void setDaemon(boolean daemon) ;
public final void interrupt();
void add(Thread t);
...

Demo:

public class ThreadGroupDemo {
    public static void main(String[] args) {
        method1();

        // method2();
    }

    private static void method2() {
        // ThreadGroup(String name)
        ThreadGroup tg = new ThreadGroup("這是一個新的組");

        MyRunnable my = new MyRunnable();
        // Thread(ThreadGroup group, Runnable target, String name)
        Thread t1 = new Thread(tg, my, "林青霞");
        Thread t2 = new Thread(tg, my, "劉意");

        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());

        t1.start();
        t2.start();

        // 通過組名稱設置后臺線程,表示該組的線程都是后臺線程
        tg.setDaemon(true);
    }

    private static void method1() {
        MyRunnable my = new MyRunnable();
        Thread t1 = new Thread(my, "林青霞");
        Thread t2 = new Thread(my, "劉意");
        // 我不知道他們屬于那個線程組,我想知道,怎么辦
        // 線程類里面的方法:public final ThreadGroup getThreadGroup()
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        // 線程組里面的方法:public final String getName()
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1);
        System.out.println(name2);
        // 通過結果我們知道了:線程默認情況下屬于main線程組
        // 通過下面的測試,你應該能夠看到,默任情況下,所有的線程都屬于同一個組
        System.out.println(Thread.currentThread().getThreadGroup().getName());

        t1.start();
        t2.start();

        // 通過組名稱設置后臺線程,表示該組的線程都是后臺線程
        Thread.currentThread().getThreadGroup().setDaemon(true);

    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }

}

7.線程池

程序啟動一個新線程成本是比較高的,因為它涉及到要與操作系統進行交互。而使用線程池可以很好的提高性能,尤其是當程序中要創建大量生存期很短的線程時,更應該考慮使用線程池。
線程池里的每一個線程代碼結束后,并不會死亡,而是再次回到線程池中成為空閑狀態,等待下一個對象來使用。

JDK5新增了一個Executors工廠類來產生線程池,有如下幾個方法

public static ExecutorService newCachedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()

這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者Callable對象代表的線程。

Demo:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 
 * 如何實現線程池的代碼呢?
 *      A:創建一個線程池對象,控制要創建幾個線程對象。
 *          public static ExecutorService newFixedThreadPool(int nThreads)
 *      B:這種線程池的線程可以執行:
 *          可以執行Runnable對象或者Callable對象代表的線程
 *          做一個類實現Runnable接口。
 *      C:調用如下方法即可
 *          Future<?> submit(Runnable task)
 *          <T> Future<T> submit(Callable<T> task)
 *      D:我就要結束,可以嗎?
 *          可以。
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        // 創建一個線程池對象,控制要創建幾個線程對象。
        // public static ExecutorService newFixedThreadPool(int nThreads)
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以執行Runnable對象或者Callable對象代表的線程
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        // 結束線程池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }

}

8.定時器

定時器是一個應用十分廣泛的線程工具,可用于調度多個定時任務以后臺線程的方式執行。

在Java中,可以通過Timer和TimerTask類來實現定義調度的功能

//Timer
public Timer()
//在指定延遲(delay)后執行任務
public void schedule(TimerTask task, long delay)

//在指定延遲(delay)后間隔(period)執行任務
public void schedule(TimerTask task,long delay,long period)

//在指定時間(time)執行任務
public void schedule(TimerTask task, Date time)

//在指定時間( firstTime)開始間隔(period)執行任務
public void schedule(TimerTask task, Date firstTime, long period)

//TimerTask
public abstract void run()
public boolean cancel()

Demo1:

import java.util.Timer;
import java.util.TimerTask;

public class TimerDemo2 {
    public static void main(String[] args) {
        // 創建定時器對象
        Timer t = new Timer();
        // 3秒后執行爆炸任務第一次,如果不成功,每隔2秒再繼續炸
        t.schedule(new MyTask2(), 3000, 2000);
    }
}

// 做一個任務
class MyTask2 extends TimerTask {
    @Override
    public void run() {
        System.out.println("beng,爆炸了");
    }
}

Demo2:

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/*
 * 需求:在指定的時間刪除我們的指定目錄(你可以指定c盤,但是我不建議,我使用項目路徑下的demo)
 */


public class TimerTest {
    public static void main(String[] args) throws ParseException {
        Timer t = new Timer();
        String s = "2014-11-27 15:45:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s);

        t.schedule(new DeleteFolder(), d);
    }
}


class DeleteFolder extends TimerTask {

    @Override
    public void run() {
        File srcFolder = new File("demo");
        deleteFolder(srcFolder);
    }

    // 遞歸刪除目錄
    public void deleteFolder(File srcFolder) {
        File[] fileArray = srcFolder.listFiles();
        if (fileArray != null) {
            for (File file : fileArray) {
                if (file.isDirectory()) {
                    deleteFolder(file);
                } else {
                    System.out.println(file.getName() + ":" + file.delete());
                }
            }
            System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,739評論 18 399
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,776評論 12 45
  • 本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。 首先講...
    李欣陽閱讀 2,482評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現javalangRunnable接口 三T...
    影馳閱讀 2,981評論 1 18
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139