1. 程式人生 > >Java多執行緒(傳智播客視訊)

Java多執行緒(傳智播客視訊)

(一)傳統執行緒技術回顧

1.建立執行緒的兩種方式

(1)建立Thread的子類,重寫run方法

(2)給Thread類傳入Runnable介面

(3)兩種建立方式的比較

第一點:通過建立執行緒方式可以看出,一個是繼承一個是實現介面,由於Java是隻能繼承一個父類,可以實現多個介面,所以說採用Runnable方式可以避免Thread方式由於Java單繼承帶來的缺陷

第二點:Runnable的程式碼可以被多個執行緒共享(Thread例項),適合於多個執行緒處理同一資源的情況

第三點:Runnable更傾向於面向物件的程式設計思想,new Thread()表示執行緒,傳入Runnable介面代表任務物件

2.Thread原始碼

public class Thread implements Runnable {
  private Runnable target;

  //方法一:無參構造
  public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
  }
  //方法二:引數為Runnable型別
  public Thread(Runnable target) {
        //呼叫方法三
        init(null, target, "Thread-" + nextThreadNum(), 0
); } //方法三 private void init(ThreadGroup g, Runnable target, String name, long stackSize) { //呼叫方法四 init(g, target, name, stackSize, null, true); } //方法四 private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean
inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security != null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); } } g.checkAccess(); if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); //給Runnable型別的引數賦值 this.target = target; setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this.stackSize = stackSize; tid = nextThreadID(); } //方法五:開啟執行緒的方法 public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { //呼叫了方法六 native方法 start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } //方法六 native方法 private native void start0(); @Override public void run() { //判斷Runnable型別的成員變數是否為空 if (target != null) { target.run(); } } }

3.Runnable原始碼

//函式式介面
@FunctionalInterface
public interface Runnable {

    public abstract void run();

}

4.建立執行緒的案例

public class TraditionalThread {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 1:Thread-0
                    System.out.println("1:" + Thread.currentThread().getName());
                    // 2:Thread-0  this這裡代表的是Thread物件
                    System.out.println("2:" + this.getName());
                }
            }
        };
        thread.start();

        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // Thread-1
                    System.out.println(Thread.currentThread().getName());
                }
            }
        });
        thread2.start();

        // thread:Thread-2  
        // Thread的子類中run方法 Runnable中的run方法 優先呼叫子類的run方法
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("runnable:" + Thread.currentThread().getName());
                }
            }
        }) {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread:" + Thread.currentThread().getName());
                }
            }
        }.start();
    }

}

5.多執行緒機制會提高程式的執行效率嗎?為什麼會有多執行緒下載呢?

​ 不會 多執行緒下載:搶佔了伺服器的頻寬 假設每個人的下載速度為20k,一個執行緒代表一個人,啟動多個執行緒代表10個人在下載,就給提供了200k的下載速度

(二)傳統定時器技術回顧

1.定時器的應用 Timer類和TimerTask類

(1)案例一:10秒後啟動,每隔3秒啟動一個

public class TraditionalTimerTest {
    public static void main(String[] args) {
        new Timer().schedule(new TimerTask() {

            @Override
            public void run() {
                System.out.println("boming!");
                // Timer-0
                System.out.println(Thread.currentThread().getName());
            }
        }, 10000, 3000);// 10秒後啟動,每隔3秒啟動一個
        while (true) {
            System.out.println(new Date().getSeconds());
            try {
                Thread.sleep(1000);// 單位毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(2)案例二: 每隔兩秒鐘啟動一次(也可以採用案例一的方法)

  • 注意:每個TimerTask()只能執行一次
public class TraditionalTimerTest2 {
    public static void main(String[] args) {
        class MyTimerTask extends TimerTask {

            @Override
            public void run() {
                System.out.println("boming!");
                new Timer().schedule(new MyTimerTask(), 2000);
            }

        }
        new Timer().schedule(new MyTimerTask(), 2000);
        while (true) {
            System.out.println(new Date().getSeconds());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)案例三:先隔兩秒啟動,再隔4秒啟動,如此迴圈

public class TraditionalTimerTest3 {
    // 內部類中不能宣告靜態變數 所以寫在外面
    private static int count = 0;

    public static void main(String[] args) {

        class MyTimerTask extends TimerTask {
            @Override
            public void run() {
                count = (count + 1) % 2;
                System.out.println("boming!");
                new Timer().schedule(new MyTimerTask(), 2000 + 2000 * count);
            }

        }
        new Timer().schedule(new MyTimerTask(), 2000);
        while (true) {
            System.out.println(new Date().getSeconds());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(三)傳統執行緒互斥技術

1.執行緒安全問題

​ 多個執行緒操作相同一份資料的時候就會出現執行緒安全的問題(銀行轉賬為例)

  • 在靜態方法中,不能new內部類的例項物件

    原因:內部類,可以訪問外部類的成員變數,呼叫靜態方法的時候,沒有建立物件,此時沒有可以訪問的成員變數,所以會報錯

案例:

public class TraditionalThreadSynchronized {

    public static void main(String[] args) {
        new TraditionalThreadSynchronized().init();
    }

    private void init() {
        Outputer outputer = new Outputer();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output("zhangsan");
                }
            }
        }).start();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output("lisi");
                }
            }
        }).start();
    }

    class Outputer {
        public void output(String name) {
            int len = name.length();
            for (int i = 0; i < len; i++) {
                System.out.print(name.charAt(i));
            }
            System.out.println();
        }
    }
}

列印結果:

zhanglisis
an
zhangsan
lisi
lzihangsan
si
zhangsan
lisi
zhlisi
angsan
lisi
zhangsan
lisi
zhangsan
lzhangsanisi
...

2.使用synchronized程式碼塊解決

  • 互斥一定要是同一個物件
class Outputer {
        public void output(String name) {
            int len = name.length();
            synchronized (Outputer.class) {
                for (int i = 0; i < len; i++) {
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }
}

3.使用synchronized方法

class Outputer {
        public synchronized void output(String name) {
            int len = name.length();
            for (int i = 0; i < len; i++) {
                System.out.print(name.charAt(i));
            }
            System.out.println();
        }
}
  • static synchronized方法鎖的是位元組碼物件

  • 互斥用的是synchronized,synchronized是檢查一把鎖物件,多個執行緒要在一段程式碼上實現同步,必須是使用同一把鎖去擋住執行緒

(四)傳統執行緒同步通訊技術

1.面試題:子執行緒迴圈10次,接著主執行緒迴圈100次,接著又回到子執行緒迴圈10次,接著再回到主執行緒又迴圈100次,如此迴圈50次,請寫出程式

  • 要用到共同資料(包括同步鎖)或共同演算法的若干個方法應該歸在同一個類身上,這種設計正好體現了高內聚和程式的健壯性
  • 鎖是上在代表要操作的資源的類的內部方法中,而不是執行緒程式碼中
public class TraditionalThreadCommunication {

    public static void main(String[] args) {
        final Business business = new Business();
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 1; i <= 50; i++) {
                    business.sub(i);
                }
            }
        }).start();
        for (int i = 1; i <= 50; i++) {
            business.main(i);
        }
    }

}

class Business {
    private boolean bShouldSub = true;

    public synchronized void sub(int i) {
        if (!bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (int j = 1; j <= 10; j++) {
            System.out.println("sub thread sequece of" + j + ",loop of" + i);
        }
        bShouldSub = false;
        this.notify();
    }

    public synchronized void main(int i) {
        if (bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 1; j <= 100; j++) {
            System.out.println("main thread sequece of" + j + ",loop of" + i);
        }
        bShouldSub = true;
        this.notify();
    }
}
  • 這種情況下使用while使得程式更加健壯
class Business {
    private boolean bShouldSub = true;

    public synchronized void sub(int i) {
        while (!bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (int j = 1; j <= 10; j++) {
            System.out.println("sub thread sequece of" + j + ",loop of" + i);
        }
        bShouldSub = false;
        this.notify();
    }

    public synchronized void main(int i) {
        while (bShouldSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 1; j <= 100; j++) {
            System.out.println("main thread sequece of" + j + ",loop of" + i);
        }
        bShouldSub = true;
        this.notify();
    }
}

(五)執行緒範圍內共享變數的概念與作用

1.執行緒內部共享資料,執行緒間資料獨立

2.執行緒內部共享資料,執行緒間資料獨立的案例

public class ThreadScopeShareData {
    private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();

    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + "has put data :" + data);
                    threadData.put(Thread.currentThread(), data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }

    static class A {
        public void get() {
            int data = threadData.get(Thread.currentThread());
            System.out.println("A from" + Thread.currentThread().getName() + "has put data :" + data);
        }
    }

    static class B {
        public void get() {
            int data = threadData.get(Thread.currentThread());
            System.out.println("B from" + Thread.currentThread().getName() + "has put data :" + data);
        }
    }

}

(六)ThreadLocal類及應用技巧

1.ThreadLocal

(1)ThreadLocal的作用和目的

​ 用於實現執行緒內的資料共享,即對於相同的程式程式碼,多個模組在同一個執行緒中執行時要共享一份資料,而在另外執行緒中執行又共享另外一份資料

  • 每個執行緒呼叫全域性ThreadLocal物件的set方法,就相當於往其內部的map中增加一條記錄,key分別是各自的執行緒,value是各自的set方法傳進去的值。線上程結束時可以呼叫ThreadLocal.clear()方法,這樣會更快釋放記憶體,不呼叫也可以,因為執行緒結束後也可以自動釋放相關的ThreadLocal變數

2.使用ThreadLocal改進上述案例

public class ThreadLocalTest {
    private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();

    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + "has put data :" + data);
                    x.set(data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }

    static class A {
        public void get() {
            int data = x.get();
            System.out.println("A from" + Thread.currentThread().getName() + "has put data :" + data);
        }
    }

    static class B {
        public void get() {
            int data = x.get();
            System.out.println("B from" + Thread.currentThread().getName() + "has put data :" + data);
        }
    }
}

3.設計執行緒內範圍內共享的物件案例

public class ThreadLocalTest {
    private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();

    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int data = new Random().nextInt();
                    System.out.println(Thread.currentThread().getName() + "has put data :" + data);
                    x.set(data);
                    MyThreadScopeData.getThreadInstance().setName("name" + data);
                    MyThreadScopeData.getThreadInstance().setAge(data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }

    static class A {
        public void get() {
            int data = x.get();
            System.out.println("A from" + Thread.currentThread().getName() + " has get data :" + data);
            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("A from" + Thread.currentThread().getName() + " getMyData :" + myData.toString());
        }
    }

    static class B {
        public void get() {
            int data = x.get();
            System.out.println("B from" + Thread.currentThread().getName() + " has get data :" + data);
            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
            System.out.println("B from" + Thread.currentThread().getName() + " getMyData :" + myData.toString());
        }
    }
}

class MyThreadScopeData {
    private MyThreadScopeData() {

    }

    // 在這個執行緒範圍內的任意地方呼叫都是一個物件,另一個執行緒呼叫則是另一個物件
    public static MyThreadScopeData getThreadInstance() {
        MyThreadScopeData instance = map.get();
        if (instance == null) {
            instance = new MyThreadScopeData();
            map.set(instance);
        }
        return instance;
    }

    private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "MyThreadScopeData [name=" + name + ", age=" + age + "]";
    }

}
  • Struts2的設計思想:每一個執行緒(請求)對應一個Action例項

    (七)多個執行緒之間共享資料的方式探討

1.如果每個執行緒執行的程式碼相同,可以使用同一個Runnable物件,這個Runnable物件中有那個共享資料

2.如果每個執行緒執行的程式碼不同,這時候需要用不同的Runnable物件,有如下兩種方式來實現這些Runnable物件之間的資料共享

(1)將共享資料封裝在另外一個物件中,然後將這個物件逐一傳遞給各個Runnable物件。每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,這樣容易實現針對該資料進行的各個操作的互斥和通訊

案例:

public class MultiThreadShareData {
    public static void main(String[] args) {

        MultiThreadShareData multiThreadShareData = new MultiThreadShareData();

        ShareData shareData = multiThreadShareData.new ShareData();

        MyRunnable1 runNable1 = multiThreadShareData.new MyRunnable1(shareData);
        MyRunnable2 runNable2 = multiThreadShareData.new MyRunnable2(shareData);
        new Thread(runNable1).start();
        new Thread(runNable2).start();
    }

    class ShareData {
        private int j = 0;

        public ShareData() {
        }

        public void increment() {
            j++;
        }

        public void decrement() {
            j--;
        }
    }

    class MyRunnable1 implements Runnable {
        private ShareData shareData;

        public MyRunnable1(ShareData shareData) {
            this.shareData = shareData;
        }

        public void run() {
            this.shareData.increment();
        }
    }

    class MyRunnable2 implements Runnable {
        private ShareData shareData;

        public MyRunnable2(ShareData shareData) {
            this.shareData = shareData;
        }

        public void run() {
            this.shareData.decrement();
        }
    }

}

(2)將這些Runnable物件作為某一個類中的內部類,共享資料作為這個外部類中的成員變數,每個執行緒對共享資料的操作方法也分配給外部類,以便實現對共享資料進行的各個操作的互斥和通訊,作為內部類的各個Runnable物件呼叫外部類的這些方法

案例:

public class MultiThreadShareData2 {
    public static void main(String[] args) {
        final ShareData shareData = new ShareData();

        new Thread(new Runnable() {
            public void run() {
                shareData.increment();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                shareData.decrement();
            }
        }).start();
    }

}

class ShareData {
    private int j = 0;

    public ShareData() {
    }

    public void increment() {
        j++;
    }

    public void decrement() {
        j--;
    }
}

​ 上面兩種方式的組合:將共享資料封裝在另外一個物件中,每個執行緒對共享資料的操作方法也分配到那個物件身上去完成,物件作為這個外部類中的成員變數或方法中的區域性變數,每個執行緒Runnable物件作為外部類中的成員變數或區域性內部類

​ 總之,要同步互斥的幾段程式碼最好是分別放在幾個獨立的方法中,這些方法再放在同一個類中,這樣比較容易實現它們之間的同步互斥和通訊

​ 極端且簡單的方式,即在任意一個類中定義一個static的變數,這將被所有執行緒共享

(八)Java5原子性操作類的應用

1.Package java.util.concurrent.atomic

​ 一個小型工具包,支援單個變數上的無鎖執行緒安全程式設計,可以對基本資料,對陣列中的基本資料,對類中的基本資料等進行操作

這裡寫圖片描述

(九)Java5執行緒併發庫的應用

1.執行緒池的概念與Executors類的應用

(1)建立執行緒池的四種方式

  • Executors類的方法

newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒 newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待 newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行 newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行(如何實現執行緒死掉後重新啟動?)

(2)關閉執行緒池

  • ExecutorService介面的方法

這裡寫圖片描述

(3)用執行緒池啟動定時器

呼叫ScheduledExecutorService的schedule方法,返回的ScheduledFuture物件可以取消任務

支援間隔重複任務的定時方式,不直接支援絕對定時方式,需要轉換成相對時間方式(date.getTime()-System.currentTimeMillis())

案例:

public class ThreadPoolTest2 {
    public static void main(String[] args) {
        // 建立一個排程執行緒池,內含有3個執行緒,實現10秒定時執行功能
        Executors.newScheduledThreadPool(3).schedule(new Runnable() {

            @Override
            public void run() {
                System.out.println("bobing!");
            }
        }, 10, TimeUnit.SECONDS);
    }
}
public class ThreadPoolTest3 {
    public static void main(String[] args) {
        // 建立一個排程執行緒池,內含有3個執行緒,實現10秒定時執後,以後每隔2秒執行一次的功能
        Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                System.out.println("bobing!");
            }
        }, 10, 2, TimeUnit.SECONDS);
    }
}

(4)執行緒池的使用案例

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 10; i++) {
            final int task = i;
            threadPool.execute(new Runnable() {

                @Override
                public void run() {
                    for (int j = 1; j <= 10; j++) {
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "is looping of" + j + "for task" + task);
                    }
                }
            });
        }
        System.out.println("all of 10 tasks have committed");
        threadPool.shutdown();
    }
}

列印結果:執行緒池中只有三個執行緒而任務有10個,前三個任務執行完才會執行後三個任務依次迴圈

all of 10 tasks have committed
pool-1-thread-1is looping of1for task1
pool-1-thread-3is looping of1for task3
pool-1-thread-2is looping of1for task2
pool-1-thread-1is looping of2for task1
pool-1-thread-2is looping of2for task2
pool-1-thread-3is looping of2for task3
...
pool-1-thread-1is looping of1for task4
pool-1-thread-2is looping of1for task6
pool-1-thread-3is looping of1for task5
pool-1-thread-1is looping of2for task4
pool-1-thread-2is looping of2for task6
pool-1-thread-3is looping of2for task5
...
  • 區域性內部類裡可訪問沒加final的區域性變數,但是對區域性變數修改時,則編譯報錯

    原因:區域性內部類最終會被編譯為一個單獨的類,其所訪問的區域性變數會成為這個類的屬性,如果訪問的一個值型別區域性變數,就會造成這個類的屬性與所訪問的區域性變數不是同一個,會造成資料不同步。所以強制要求區域性變數必須為final,避免資料不同步

(十)Callable與Future的應用

1.Callable&Future

Future取得的結果型別和Callable返回的結果型別必須一致,這是通過泛型來實現的

Callable要採用ExecutorSevice的submit方法提交,返回的future物件可以取消任務

public class CallableAndFuture {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<String> future = threadPool.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                Thread.sleep(2000);
                return "hello";
            }

        });
        System.out.println("等待結果");
        System.out.println("拿到結果:"+future.get());
    }
}

CompletionService用於提交一組Callable任務,其take方法返回已完成的一個Callable任務對應的Future物件

public class CallableAndFuture2 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool);
        for (int i = 0; i < 10; i++) {
            final int seq = i;
            completionService.submit(new Callable<Integer>() {

                @Override
                public Integer call() throws Exception {
                    Thread.sleep(new Random().nextInt(5000));
                    return seq;
                }
            });
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(completionService.take().get());
        }
    }
}

(十一)Java5的執行緒鎖技術

1.Lock

​ Lock比傳統執行緒模型中的synchronized方式更加面向物件,與生活中的鎖類似,鎖本身也應該是一個物件。兩個執行緒執行的程式碼片段要實現同步互斥的效果,它們必須用同一個Lock物件。鎖是上在代表要操作的資源的類的內部方法中,而不是執行緒程式碼中

public class LockTest {

    public static void main(String[] args) {
        new LockTest().init();
    }

    private void init() {
        Outputer outputer = new Outputer();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output("zhangsan");
                }
            }
        }).start();
        new Thread(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    outputer.output("lisi");
                }
            }
        }).start();
    }

    class Outputer {
        Lock lock = new ReentrantLock();

        public void output(String name) {
            int len = name.length();
            lock.lock();
            try {
                for (int i = 
            
           

相關推薦

Java執行(視訊)

(一)傳統執行緒技術回顧 1.建立執行緒的兩種方式 (1)建立Thread的子類,重寫run方法 (2)給Thread類傳入Runnable介面 (3)兩種建立方式的比較 第一點:通過建立執行緒方式可以看出,一個是繼承一個是實現介面,由

Java 執行值有三種方式,以及另類的第四種方式

現在博主的需求是:有可能在同一個執行緒類執行不一樣的程式。上邊兩個紅框中的cron4j排程器使用的是一個,根據引數不同來執行的。如果我點選後邊的手動執行一次,按照我上邊給出的java程式碼是無法實現的。看下邊的新的程式碼: (adsbygoogle = window.adsbygoo

WebService-CXF-Spring 基於web的cxf(三)------參考視訊

1      WebService-CXF-Spring 基於web的cxf 1.1    開發cxf的web專案:--原始碼eclipse無法訪問請使用myeclipse l  由於cxf的web專案已經集成了Spring所以,cxf的服務類都是在spring的配置檔

JDBC學習記錄(視訊

1、概念JDBC:java Data Base Connectivity ,即java資料庫連結JDBC是一套API,也就是sun公司定義的類或者介面。而驅動是資料庫公司定義的類庫,實現了sun公司規定的介面。2、JDBC開發步驟a、註冊驅動:告知JVM使用的是哪一個資料庫的

JAVA_Lesson14(筆記之執行

多執行緒間通訊-示例之間是很多執行緒都在執行同一項任務,但是現在他們處理的任務不同(處理的還是同一資源)。執行緒間通訊-等待喚醒機制(握手機制)應該有個判斷裡面是否有資源的標記flag,判斷是否有資源。如果裡面有資源的話,應該輸出開始操作,而輸入應該先釋放執行權,再釋放執行資

成都Java/PHP培訓就業率高

lan 培訓課程 ref -s size unit str 學生 http 依據傳智播客的數據統計,傳智播客的學員有五分之中的一個的能在畢業前找到愜意的工作,一半的學員能在畢業後一個月之內找到愜意的工作,一般在畢業後兩個月之內絕大多數同學都能找到愜意的工作。而且傳智播客

【藏龍臥虎】成都Java就業班火爆開班!

技術 找工作 java培訓 borde targe 學習 rgb idt pro 今天早晨成都被一篇烏雲籠罩,沒想到卻是一個陽光普照的日子。今天傳智播客成都java培訓中心舉行了Java就業班開班典禮,看似普通的一個班級卻個個非比平常,學員們不僅Professiona

關於線程編寫的小技巧--觀張孝祥老師視頻有感

not 線程 zed 技巧 寫到 保持 但是 判斷 notify 在此,真的很感謝傳智播客張孝祥老師,雖然已離去,但是還是要感謝。多線程這一塊幫助了很多。(觀看的是公開課視頻) 一般我先可以寫單線程版程序的,但一定要考慮清楚,將會發生競態條件的資源寫到同一個類裏面。 然後考

劉意_2015年Java基礎視訊-深入淺出精華版 筆記(day01~day10)

本筆記是個人筆記+摘錄筆記相結合,非完全原創 day01 win 7系統開啟DOS有趣方法:按住shift+右鍵,單擊“在此處開啟命令視窗”(注意:在此處可以是任何的資料夾,不一定是桌面)用DOS刪除的檔案不可以在回收站恢復?!!常用DOS命令d: 回車 碟符切換dir(directory)

2018Java培訓視訊課程下載

2018年11月01日 21:12:45 qq_43580805 閱讀數:4 標籤: 程式設計 資料

C語言視訊第二季(第一季基礎上增加諸C語言案例講解,有效下載期為10.5-10.10關閉

                卷 backup 的資料夾 PATH 列表卷序列號為 00000025 D4A8:14B0J:.│  1.txt│  c語言經典案例效果圖示.doc│  ├─1傳智播客_尹成_C語言從菜鳥到高手_第一章C語言概述A│  ├─文件│  │      第1講 C語言第一階段.doc

2018 Python / Java 全套教程!

之前給大家分享過一波黑馬的 2017 年就業班的全套視訊教程,相信看過的朋友應該都覺得不錯,這次小B又找到了最新版 2018 年基礎班+就業班全套視訊。 視訊內容畫面基本都是 1440 * 900,最主要的是講課特別專業,廢話少。有需要的同學文末自取。 基礎班系

-劉意-java深入淺出精華版學習筆記Day01

計算機基礎知識:開啟控制行的方法:win+R,cmd回車Tips:1.切換碟符的時候大小寫無所謂。2.安裝軟體的時候在非系統盤裡建立一個新的目錄,把所有的程式檔案放到這個目錄起來,這樣既不佔系統盤空間,也不會把非系統盤搞得亂七八糟。3.刪除帶內容的資料夾:rd後加/s關鍵字,

java執行實現斷點續下載

public class DownloadThread extends Thread {private int id;private int startindex;private int endindex;private String path;static int threadfinishedcount=0

java 執行解壓檔案

舉個公司專案開發遇到的一個簡單例子,使用者上傳壓縮檔案到伺服器後,要對該壓縮包進行兩個操作,一是將該壓縮包複製到指定目錄,一是將該壓縮包解壓到另一指定目錄,最終響應使用者提示檔案上傳成功。如果壓縮包很大的話,上傳後進行的複製和解壓功能也會佔用很長時間,使用者就會等待很長的

劉意_2015年Java基礎視訊-深入淺出精華版 筆記(2015年10月25日23:28:50)

day01 win 7系統開啟DOS有趣方法:按住shift+右鍵,單擊“在此處開啟命令視窗”(注意:在此處可以是任何的資料夾,不一定是桌面) 用DOS刪除的檔案不可以在回收站恢復?!! 常用DOS命令 d: 回車 碟符切換 dir(direct

-劉意-java深入淺出精華版學習筆記Day07

成員變數和區域性變數的區別:成員變數:類中方法外               堆記憶體中                隨著物件的建立存在                隨著物件的消失而消失                有預設初始化值區域性變數:方法定義中或者方法宣告上  

--劉意Java基礎視訊-深入淺出精華版

package cn.itcast.test; import java.util.Scanner; import cn.itcast.dao.UserDao; import cn.itcast.dao.impl.UserDaoImpl; import cn.itcast.game.Gues

-Java學習筆記day11

Object:類 Object 是類層次結構的根類。每個類都使用 Object 作為超類。  * 每個類都直接或者間接的繼承自Object類。  *   * Object類的方法:  *         public int hashCode():返回該物件的雜湊碼值。  *             注意:

Java基礎入門 pdf

下載地址:網盤下載內容簡介  《Java基礎入門》從初學者的角度詳細講解了Java開發中重點用到的多種技術。全書共11章,包括Java開發環境的搭建及其執行機制、基本語法、面向物件的思想,採用典型翔實的