下面小編就為大家帶來一篇淺談Java多執行緒實作及同步互斥通訊。小編覺得蠻不錯的,現在就分享給大家,也給大家做個參考。一起跟著小編過來看看吧
Java多執行緒深入理解#本文主要從三個面向了解並掌握多執行緒:
1 . 多執行緒的實作方式,透過繼承Thread類別和透過實作Runnable介面的方式以及異同點。
2. 多執行緒的同步與互斥中synchronized的使用方法。
3. 多執行緒的通訊中的notify(),notifyAll(),及wait(),的使用方法,以及簡單的生成者和消費者的程式碼實作。
下面來具體的講解Java中的多執行緒:
一:多執行緒的實作方式
透過繼承Threa類別來實作多執行緒主要分為以下三個步驟:
#第一步:繼承Thread,實作Thread類別中的run()方法。
第二步:定義一個Thread子類別的實例。
第三步:透過呼叫Thread類別的start()方法來啟動執行緒。
下面是簡單的程式碼實作:
class myThread extends Thread{ int n=100; public void run() { while (true) { if (n > 0) { System.out.println(":"Thread.currentThread().getName() + "..." + n--); } else { break; } } } } public class ThreadTest { public static void main(String[] args) { myThread mythread=new myThread(); mythread.setName("子线程"); mythread.start(); } }
上面執行緒中用到的幾個方法:Thread.currentThraed().getName()方法得到當前執行緒的名字。 mythread.setName(“子執行緒”);為mythread執行緒重新命名為「子執行緒」。
透過實作Runnable 介面來實作多執行緒主要分為以下幾個步驟:
第一步:實作Runnable介面中的run( )方法。產生一個Runnable的實例。
第二步:定義一個Thread類,並且將上面的Runnable實例傳給Thread類別的建構方法。
第三步:透過呼叫Thread類別的start()方法來啟動執行緒。
以下是簡單的程式碼實作:
class myRunnable implements Runnable{ int n=100; public void run() { while (true) { if (n > 0) { System.out.println(":"Thread.currentThread().getName() + "..." + n--); } else { break; } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable = new myRunnable(); Thread mythread=new Thread(myrunnable); mythread.setName("子线程"); mythread.start(); } }
既然透過繼承Thread類,和實作Runnable方法都能實現多執行緒的運行那麼兩種方式到底有什麼不同呢?下面透過一個買票的類別來看看到底二者有什麼不同:
假設售票處總共有100張票,透過三個窗口來進行售票也就是說我們要開闢三個不同的線程來實現買票:首先看看透過Thread類別來實現買票的方式:
class myThread extends Thread{ int n=100; public void run() { while (true) { if (n > 0) { System.out.println(":"Thread.currentThread().getName() + "..." + n--); } else { break; } } } } public class ThreadTest { public static void main(String[] args) { myThread m1=new myThread(); myThread m2=new myThread(); myThread m3=new myThread(); m1.setName("窗口1"); m2.setName("窗口2"); m3.setName("窗口3"); m1.start(); m2.start(); m3.start(); } }
結果太長了我不展示了,可以看到原本三個窗口共同買100張票的,但是結果每個視窗都買了100張票,這個很好理解因為每個視窗都是一個獨立的物件,都有自己的n=100。所以透過Thread類別來實現買票功能是不可行的,其實用Thread方法來實現多線程,其中每個線程執行的程式碼並不是同一段程式碼。
下面來看看實作Runnable介面是怎麼實作買票功能的:
class myRunnable implements Runnable{ int n=100; public void run() { while (true) { if (n > 0) { System.out.println(":"Thread.currentThread().getName() + "..." + n--); } else { break; } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable=new myRunnable(); Thread m1=new Thread(myrunnable); Thread m2=new Thread(myrunnable); Thread m3=new Thread(myrunnable); m1.setName("窗口1"); m2.setName("窗口2"); m3.setName("窗口3"); m1.start(); m2.start(); m3.start(); } }
可以看出上面三個執行緒公用的是同一個Runnable的子類,所以只是開闢三個執行緒來執行同一段runnable的程式碼。所以不會有買300張票的情況。但是這個程式還是有問題的當講到了後面的線程的同步與互斥後我們再來完善這段程式。
二:多執行緒的同步與互斥中synchronized與volatile的使用方法。
現在截取一段上面程式碼執行過程中出現的問題並且分析一下問題是如何產生的,再來透過synchronied來解決此問題。
:視窗2…1
:視窗1…1
:視窗3…1
:視窗1…2
:視窗2…2
:視窗1… 3
:視窗3…2
:視窗1…4
:視窗2…3
:視窗1…5
:視窗3…3
:視窗1…6
:視窗2…4
上面程式碼的結果是透過實作Runnable介面產生的,上面視窗1,視窗2,視窗3,同時產生1是怎麼產生的呢?
int n=100; public void run() { while (true) { if (n > 0) { System.out.println(":"Thread.currentThread().getName() + "..." + n--); } else { break; } }
這是三個執行緒共同執行的同一段程式碼,上面結果產生的一個原因可能是當視窗2執行完輸出i=1時此時虛擬機器執行了視窗2,視窗2執行輸出I,此時的i還未執行++,所以i的值還是1,此時虛擬機又把執行權給了窗口3,這時候窗口3輸出的i仍然是1,程式產生上面問題的主要原因是存在公共變數i,i的值在程式執行的過程中未保持同步。上面的for迴圈體應該單獨執行完之後才能讓其他的執行緒搶佔虛擬機器。 Synchronized關鍵字就是用來實作保證執行緒在執行一段公共資源是不被其他執行緒搶佔。
被synchronized修飾的程式碼區塊稱為同步程式碼區塊,被synchronized修飾的方法稱為同步方法。以下透過加入synchronized關鍵字來實現買票功能:
class myRunnable implements Runnable { int n = 100; public void run() { while (true) { synchronized (this) { if (n > 0) { if (n % 10 == 0) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(":" + Thread.currentThread().getName() + "..." + n--); } else { break; } } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable = new myRunnable(); Thread m1 = new Thread(myrunnable); Thread m2 = new Thread(myrunnable); Thread m3 = new Thread(myrunnable); m1.setName("窗口1"); m2.setName("窗口2"); m3.setName("窗口3"); m1.start(); m2.start(); m3.start(); } }
此時是可以正確的完成售票功能的。
上面代码中synchronized(this)中的this代表的是当前的对象,因为三个线程执行的都是myRunnable 的对象,所以三个线程公用的是同一个锁,其实这个this可以用任何的对象来代替,一般我们可以 String str=new String(“”);虽然str的值为空字符串,但是也是一个对象。Synchronized实现互斥的原理是每一个对象都有一个特定的变量值,当任何一个线程调用了synchronized想要进入公共资源区时,先判断该变量的值,若该变量的值为0则可以进入公共资源区,进程在进入公共资源区之前先把对象的中的该变量值变为1,出同步区后再将该变量的值变为0,从而实现线程互斥访问公共资源。
三:多线程的通讯中的notify(),notifyAll(),及wait(),的使用方法,以及简单的生成者和消费者的代码实现。
在讲解notify(),notifyAll(),wait()之前,先看看生产者和消费者问题:生产者生产面包,消费者消费面包,但是存放面包的容器有限,生产者一次最多只能生产20个面包,消费者每次在容器中拿一个面包。通过分析可以知道,当生产者生产了20个面包之后必须停下来,等容器里的面包数目小于20个时再继续生产,消费者看到容器里面面包个数为0时也必须停下来,等到有面包时才能消费。这时候就涉及到了生产者和消费者的通信。notify()是用于唤醒等待队列中的线程,wait()用于阻塞当前线程。Notify和wait()都必须用于synchronized修饰的同步代码块或同步方法中。
下面直接看生产者消费者代码。
class Consumer implements Runnable { Clerk clerk; Consumer(Clerk clerk) { this.clerk = clerk; } public void run() { while(true) clerk.consumeProduct(); } } class Producter implements Runnable { Clerk clerk; Producter(Clerk clerk) { this.clerk = clerk; } public void run() { while(true) clerk.addProduct(); } } class Clerk { int product ; public synchronized void consumeProduct() { while (true) { if (product <= 0) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { product--; notifyAll(); System.out.println("消费者消费了:" + product); } } } public synchronized void addProduct() { if (product > 20) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { product++; notifyAll(); System.out.println("生产者生产了:" + product); } } } public class Test { public static void main(String[] args) { Clerk clerk=new Clerk(); Consumer consumer=new Consumer(clerk); Thread c=new Thread(consumer); Producter producter=new Producter(clerk); Thread p=new Thread(producter); c.start(); p.start(); } }
【相关推荐】
以上是簡述多執行緒實作及同步互斥通訊的詳細內容。更多資訊請關注PHP中文網其他相關文章!