Java編程基礎26——多線程下

1_多線程(單例設計模式) *

  • 單例設計模式:保證類在內存中只有一個對象。
  • 如何保證類在內存中只有一個對象呢?java

    • (1)控制類的建立,不讓其餘類來建立本類的對象。private
    • (2)在本類中定義一個本類的對象。Singleton s;
    • (3)提供公共的訪問方式。 public static Singleton getInstance(){return s}
  • 單例寫法三種:
public class Demo5_Singleton {
    //單列設計模式,保證類在內存中只有一個對象
    public static void main(String[] args) {    
        Singleton s1 = Singleton.s;        //成員變量私有,不能經過類名.調用,s和s1指向的是同一個對象
//        Singleton.s = null;
        Singleton s2 = Singleton.s;                
        System.out.println(s1 == s2);        //比較引用地址值
        
        /*Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);*/
    }
}

//(1)餓漢式-簡單直接-空間換時間-開發中使用
/*class Singleton {
    //1.私有構造方法,其餘類不能訪問該構造方法
     private Singleton() {}
    //2.建立本類對象
    public static Singleton s = new Singleton();
    //3.對外提供公共的訪問方法
    public static Singleton getInstance() {                //獲取實例(對象)
        return s;
    }
}*/

//(2)懶漢式-單例延遲加載模式-時間換空間-開發不用
/*class Singleton {
    //1.私有構造方法,其餘類不能訪問該構造方法
     private Singleton() {}
    //2.聲明一個引用
    public static Singleton s ;
    //3.對外提供公共的訪問方法
    public static Singleton getInstance() {                //獲取實例(對象)
        if(s == null) {
            //線程1等待,線程2等待,有可能建立多個對象
            s = new Singleton();
        }
        return s;
    }
}*/

//(3)第三種格式-使用final修飾
class Singleton {
    //1.私有構造方法,其餘類不能訪問該構造方法
     private Singleton() {}
    //2.聲明一個引用
    public final static Singleton s = new Singleton();
}

2_多線程(Runtime類)

  • Runtime類是一個單例類
import java.io.IOException;
public class Demo6_Runtime {
    public static void main(String[] args) throws IOException {
        Runtime r = Runtime.getRuntime();            //獲取運行時對象
//        r.exec("shutdown -s -t 3000");
        r.exec("shutdown -a");
    }
}

3_多線程(Timer) *

  • Timer類:計時器
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Demo7_Timer {
    public static void main(String[] args) throws InterruptedException {
        Timer t = new Timer();
        //在指定時間安排指定任務:安排任務.執行時間.重複執行時間
        //年=當前-1900  月=當前-1
        t.schedule(new MytimerTask(), new Date(118, 9, 11, 11, 57, 30),3000);
        
        while(true) {
            Thread.sleep(1000);
            System.out.println(new Date());
        }
    }
}

class MytimerTask extends TimerTask {
    @Override
    public void run() {
        System.out.println("起牀上班");
    }
}

4_多線程(兩個線程間的通訊) *

  • 1.何時須要通訊程序員

    • 多個線程併發執行時, 在默認狀況下CPU是隨機切換線程的
    • 若是咱們但願他們有規律的執行, 就可使用通訊, 例如每一個線程執行一次打印
  • 2.怎麼通訊設計模式

    • 若是但願線程等待, 就調用wait()
    • 若是但願喚醒等待的線程, 就調用notify();
    • 這兩個方法必須在同步代碼中執行, 而且使用同步鎖對象來調用
public class Demo8_Notify {
    //等待喚醒機制
    public static void main(String[] args) {
        final Printer p = new Printer();
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

//等待喚醒機制
class Printer {
    private int flag = 1;
    public void print1() throws InterruptedException {    
        synchronized(this) {
            if(flag != 1) {                //當前線程等待
                this.wait();
            }
            System.out.print("我");
            System.out.print("愛");
            System.out.print("寫");
            System.out.print("代");
            System.out.print("碼");
            System.out.print("\r\n");
            flag = 2;
            this.notify();            //隨機喚醒單個等待線程
        }
    }
    
    public void print2() throws InterruptedException {
        synchronized(this) {
            if(flag != 2) {
                this.wait();                
            }
            System.out.print("學");
            System.out.print("習");
            System.out.print("編");
            System.out.print("程");
            System.out.print("\r\n");
            flag = 1;
            this.notify();
        }
    }
}

5_多線程(三個或三個以上間的線程通訊)

  • 1.多個線程通訊的問題多線程

    • notify()方法是隨機喚醒一個線程
    • notifyAll()方法是喚醒全部線程
    • JDK5以前沒法喚醒指定的一個線程
    • 若是多個線程之間通訊, 須要使用notifyAll()通知全部線程, 用while來反覆判斷條件
  • 2.在同步代碼塊中,用哪一個對象鎖,就用哪一個對象調用wait方法
  • 3.爲何wait方法和notify方法定義在Object這類中?併發

    • 由於鎖對象能夠是任意對象,Object是全部類的基類。
  • 4.sleep方法和wait方法的區別?ide

    • a: sleep方法必須傳入參數,參數就是時間值,時間到了自動醒來。
    • b: wait方法能夠傳入也能夠不傳入參數,傳入參數就是在參數的時間結束後等待,不傳入參數就是直接等待。
    • c: sleep方法在同步函數或同步代碼塊中,不釋放鎖。
    • d: wait方法在同步函數或者同步代碼塊中,釋放鎖。
public class Demo9_NotifyAll {
    public static void main(String[] args) {
        final Printer2 p = new Printer2();
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print3();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

class Printer2 {
    private int flag = 1;
    public void print1() throws InterruptedException {    
        synchronized(this) {
            while(flag != 1) {                //當前線程等待
                this.wait();
            }
            System.out.print("我");
            System.out.print("愛");
            System.out.print("寫");
            System.out.print("代");
            System.out.print("碼");
            System.out.print("\r\n");
            flag = 2;
            this.notifyAll();            //隨機喚醒單個等待線程
        }
    }
    
    public void print2() throws InterruptedException {
        synchronized(this) {
            while(flag != 2) {    
                this.wait();                    //線程2等待                
            }
            System.out.print("學");
            System.out.print("習");
            System.out.print("編");
            System.out.print("程");
            System.out.print("\r\n");
            flag = 3;
            this.notifyAll();
        }
    }
    
    public void print3() throws InterruptedException {
        synchronized(this) {
            while(flag != 3) {
                this.wait();            //if語句在哪裏等待,在哪裏喚醒,while是循環判斷
            }
            System.out.print("天");
            System.out.print("天");
            System.out.print("向");
            System.out.print("上");
            System.out.print("\r\n");
            flag = 1;
            this.notifyAll();
        }
    }
}

6_多線程(JDK1.5的新特性互斥鎖) *

  • 1.同步函數

    • 使用ReentrantLock類的lock()和unlock()方法進行同步
  • 2.通訊佈局

    • 使用ReentrantLock類的newCondition()方法能夠獲取Condition對象
    • 須要等待的時候使用Condition的await()方法, 喚醒的時候用signal()方法
    • 不一樣的線程使用不一樣的Condition, 這樣就能區分喚醒的時候找哪一個線程了
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Demo91_ReentrantLock {
    public static void main(String[] args) {
        final Printer3 p = new Printer3();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread() {
            public void run() {
                while(true) {
                    try {
                        p.print3();
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
class Printer3 {
    private ReentrantLock r = new ReentrantLock();
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();

    private int flag = 1;
    public void print1() throws InterruptedException {    
        r.lock();                            //獲取鎖
            if(flag != 1) {                //當前線程等待
                c1.await();
            }
            System.out.print("1");
            System.out.print("1");
            System.out.print("1");
            System.out.print("1");
            System.out.print("\r\n");
            flag = 2;
            c2.signal();
        r.unlock();                        //釋放鎖
    }
    
    public void print2() throws InterruptedException {
        r.lock();
            if(flag != 2) {    
                c2.await();            
            }
            System.out.print("2");
            System.out.print("2");
            System.out.print("2");
            System.out.print("2");
            System.out.print("\r\n");
            flag = 3;
            c3.signal();
        r.unlock();
    }
    
    public void print3() throws InterruptedException {
        r.lock();
            if(flag != 3) {
                c3.await();
            }
            System.out.print("3");
            System.out.print("3");
            System.out.print("3");
            System.out.print("3");
            System.out.print("\r\n");
            flag = 1;
            c1.signal();
        r.unlock();
    }
}

7_多線程(線程組的概述和使用)(瞭解)

  • A:線程組概述性能

    • Java中使用ThreadGroup來表示線程組,它能夠對一批線程進行分類管理,Java容許程序直接對線程組進行控制。
    • 默認狀況下,全部的線程都屬於主線程組。測試

      • public final ThreadGroup getThreadGroup()//經過線程對象獲取他所屬於的組
      • public final String getName()//經過線程組對象獲取他組的名字
    • 咱們也能夠給線程設置分組

      • 1,ThreadGroup(String name) 建立線程組對象並給其賦值名字
      • 2,建立線程對象
      • 3,Thread(ThreadGroup?group, Runnable?target, String?name)
      • 4,設置整組的優先級或者守護線程
    • B:案例演示

      • 線程組的使用,默認是主線程組
      • 線程組的使用,本身設定線程組
public class Demo4_ThreadGroup {
    public static void main(String[] args) {
//        demo1();
        //本身設定線程組
        ThreadGroup tg = new ThreadGroup("我是一個新的線程組");        //建立新的線程組
        MyRunnable2 mr = new MyRunnable2();                            //建立Runnable的子類對象
        
        Thread t1 = new Thread(tg, mr, "張三");                        //將線程t1放在組中
        Thread t2 = new Thread(tg, mr, "李四");                        //將線程t2放在組中
        
        System.out.println(t1.getThreadGroup().getName());            //獲取組名
        System.out.println(t2.getThreadGroup().getName());
        
        tg.setDaemon(true);
    }

    private static void demo1() {
        //默認是主線程組
        MyRunnable2 mr = new MyRunnable2();
        Thread t1 = new Thread(mr, "張三");
        Thread t2 = new Thread(mr, "李四");
        
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        
        System.out.println(tg1.getName());            //默認的是主線程
        System.out.println(tg2.getName());
    }
}

class MyRunnable2 implements Runnable {

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

8_多線程(線程的五種狀態) *

  • 看圖說話
  • 新建,就緒,運行,阻塞,死亡

圖片描述

9_多線程(線程池的概述和使用)(瞭解)

  • A:線程池概述

    • 程序啓動一個新線程成本是比較高的,由於它涉及到要與操做系統進行交互。而使用線程池能夠很好的提升性能,尤爲是當程序中要建立大量生存期很短的線程時,更應該考慮使用線程池。線程池裏的每個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對象來使用。在JDK5以前,咱們必須手動實現本身的線程池,從JDK5開始,Java內置支持線程池
  • B:內置線程池的使用概述

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

      • public static ExecutorService newFixedThreadPool(int nThreads)
      • public static ExecutorService newSingleThreadExecutor()
      • 這些方法的返回值是ExecutorService對象,該對象表示一個線程池,能夠執行Runnable對象或者Callable對象表明的線程。它提供了以下方法
      • Future<?> submit(Runnable task)
      • <T> Future<T> submit(Callable<T> task)
    • 使用步驟:

      • 建立線程池對象
      • 建立Runnable實例
      • 提交Runnable實例
      • 關閉線程池
    • C:案例演示

      • 提交的是Runnable
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo93_Executors {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);    //建立線程池
        // 能夠執行Runnable對象或者Callable對象表明的線程
        pool.submit(new MyRunnable2());            //將線程放進池子裏並執行
        pool.submit(new MyRunnable2());
        
        pool.shutdown();        //關閉線程池
    }
}

10_多線程(多線程程序實現的方式3)(瞭解)

  • 提交的是Callable
  • 多線程程序實現的方式3的好處

    • 能夠有返回值
    • 能夠拋出異常
  • 弊端:

    • 代碼比較複雜,因此通常不用
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 Demo94_Callable {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService pool = Executors.newFixedThreadPool(2);    //建立線程池
        Future<Integer> f1 = pool.submit(new MyCallable(100));            //將線程放進池子裏並執行
        Future<Integer> f2 = pool.submit(new MyCallable(50));
        
        System.out.println(f1.get());
        System.out.println(f2.get());
        
        pool.shutdown();        //關閉線程池
    }
}

class MyCallable implements Callable<Integer> {
    private int num;
    public MyCallable(int num) {
        this.num = num;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i = 1; i <= num; i++) {
            sum += i;
        }
        return sum;
    }
}

11_設計模式(簡單工廠模式概述和使用)(瞭解)

  • A:簡單工廠模式概述

    • 又叫靜態工廠方法模式,它定義一個具體的工廠類負責建立一些類的實例
  • B:優勢

    • 客戶端不須要在負責對象的建立,從而明確了各個類的職責
  • C:缺點

    • 這個靜態工廠類負責全部對象的建立,若是有新的對象增長,或者某些對象的建立方式不一樣,就須要不斷的修改工廠類,不利於後期的維護
  • D:案例演示

    • 動物抽象類:public abstract Animal { public abstract void eat(); }
    • 具體狗類:public class Dog extends Animal {}
    • 具體貓類:public class Cat extends Animal {}
    • 開始,在測試類中每一個具體的內容本身建立對象,可是,建立對象的工做若是比較麻煩,就須要有人專門作這個事情,因此就知道了一個專門的類來建立對象。
public abstract class Animal {
    public abstract void eat();
}
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("貓吃魚");
    }
}
public class AnimalFactory {
    /*public static Dog createDog() {
        return new Dog();
    }
    
    public static Cat CreateCat() {
        return new Cat();
    }*/
    //發現方法會定義不少,複用性差
    
    public static Animal createAnimal(String name) {
        if("dog".equals(name)) {
            return new Dog();
        }else if("cat".equals(name)) {
            return new Cat();
        }else {
        return null;
        }
    }
}
public class Test {
    public static void main(String[] args) {
//        Dog d = AnimalFactory.createDog();
        Dog d = (Dog) AnimalFactory.createAnimal("dog");
        d.eat();
        
        Cat c = (Cat) AnimalFactory.createAnimal("cat");
        c.eat();
    }
}

12_設計模式(工廠方法模式的概述和使用)(瞭解)

  • A:工廠方法模式概述

    • 工廠方法模式中抽象工廠類負責定義建立對象的接口,具體對象的建立工做由繼承抽象工廠的具體類實現。
  • B:優勢

    • 客戶端不須要在負責對象的建立,從而明確了各個類的職責,若是有新的對象增長,只須要增長一個具體的類和具體的工廠類便可,不影響已有的代碼,後期維護容易,加強了系統的擴展性
  • C:缺點

    • 須要額外的編寫代碼,增長了工做量
  • D:案例演示
動物抽象類:public abstract Animal { 
                       public abstract void eat(); 
                   }
        工廠接口:public interface Factory {
                    public abstract Animal createAnimal();
                 }
        具體狗類:public class Dog extends Animal {
                    public void eat() {
                        System.out.println("狗吃肉");
                    }
                 }
        具體貓類:public class Cat extends Animal {
                    public void eat() {
                        System.out.println("貓吃魚");
                    }
                 }
                 
    開始,在測試類中每一個具體的內容本身建立對象,可是,建立對象的工做若是比較麻煩,就須要有人專門作這個事情,
因此就知道了一個專門的類來建立對象。發現每次修改代碼太麻煩,用工廠方法改進,針對每個具體的實現提供一個具體工廠。

        狗工廠:public class DogFactory implements Factory {
                    public Animal createAnimal() {
                        return new Dog();
                    }
                }
        貓工廠:public class CatFactory implements Factory {
                   public Animal createAnimal() {
                        return new Dog();
                    }
               }
        測試類:
        public class Test {
            public static void main(String[] args) {
                DogFactory df = new DogFactory();
                Dog d = (Dog) df.createAnimal();
                d.eat();
            }
        }

13_GUI(如何建立一個窗口並顯示)

  • Graphical User Interface(圖形用戶接口)。
Frame  f = new Frame(「my window」);
        f.setLayout(new FlowLayout());//設置佈局管理器
        f.setSize(500,400);//設置窗體大小
        f.setLocation(300,200);//設置窗體出如今屏幕的位置
        f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        f.setVisible(true);

14_GUI(佈局管理器)

  • FlowLayout(流式佈局管理器)

    • 從左到右的順序排列。
    • Panel默認的佈局管理器。
  • BorderLayout(邊界佈局管理器)

    • 東,南,西,北,中
    • Frame默認的佈局管理器。
  • GridLayout(網格佈局管理器)

    • 規則的矩陣
  • CardLayout(卡片佈局管理器)

    • 選項卡
  • GridBagLayout(網格包佈局管理器)

    • 非規則的矩陣

15_GUI(窗體監聽)

Frame f = new Frame("個人窗體");
    //事件源是窗體,把監聽器註冊到事件源上
    //事件對象傳遞給監聽器
    f.addWindowListener(new WindowAdapter() {
              public void windowClosing(WindowEvent e) {
                         //退出虛擬機,關閉窗口
            System.exit(0);
        }
    });

16_GUI(鼠標監聽)

17_GUI(鍵盤監聽和鍵盤事件)

18_GUI(動做監聽)

19_設計模式(適配器設計模式) *

  • a.什麼是適配器

    • 在使用監聽器的時候, 須要定義一個類事件監聽器接口.
    • 一般接口中有多個方法, 而程序中不必定全部的都用到, 但又必須重寫, 這很繁瑣.
    • 適配器簡化了這些操做, 咱們定義監聽器時只要繼承適配器, 而後重寫須要的方法便可.
  • b.適配器原理

    • 適配器就是一個類, 實現了監聽器接口, 全部抽象方法都重寫了, 可是方法全是空的.
    • 適配器類須要定義成抽象的,由於建立該類對象,調用空方法是沒有意義的
    • 目的就是爲了簡化程序員的操做, 定義監聽器時繼承適配器, 只重寫須要的方法就能夠了.

20_GUI(須要知道的)

  • 事件處理

    • 事件: 用戶的一個操做
    • 事件源: 被操做的組件
    • 監聽器: 一個自定義類的對象, 實現了監聽器接口, 包含事件處理方法,把監聽器添加在事件源上, 當事件發生的時候虛擬機就會自動調用監聽器中的事件處理方法
public class Demo1_Adapter {
/*
    * @param args
    * 適配器設計模式
    * 魯智深
    * */
    public static void main(String[] args) {
        
    }
}
interface 和尚 {
    public void 打坐();
    public void 唸經();
    public void 撞鐘();
    public void 習武();
}

abstract class 天罡星 implements 和尚{            //聲明成抽象的緣由是,不想讓其餘類建立本類對象,由於建立也沒有意義

    @Override
    public void 打坐() {
    }

    @Override
    public void 唸經() {
    }

    @Override
    public void 撞鐘() {
    }

    @Override
    public void 習武() {
    }
}

class 魯智深 extends 天罡星{
    public void 習武() {
        System.out.println("倒拔垂楊柳");
        System.out.println("拳打鎮關西");
        System.out.println("大鬧野豬林");
        System.out.println("..........");
    }
}
相關文章
相關標籤/搜索