搜尋
首頁Javajava教程簡述多執行緒實作及同步互斥通訊

下面小編就為大家帶來一篇淺談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();
  }
}

【相关推荐】

1.Java免费视频教程

2.全面解析Java注解

3.阿里巴巴Java开发手册

以上是簡述多執行緒實作及同步互斥通訊的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
带你搞懂Java结构化数据处理开源库SPL带你搞懂Java结构化数据处理开源库SPLMay 24, 2022 pm 01:34 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

Java集合框架之PriorityQueue优先级队列Java集合框架之PriorityQueue优先级队列Jun 09, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

完全掌握Java锁(图文解析)完全掌握Java锁(图文解析)Jun 14, 2022 am 11:47 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

一起聊聊Java多线程之线程安全问题一起聊聊Java多线程之线程安全问题Apr 21, 2022 pm 06:17 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

详细解析Java的this和super关键字详细解析Java的this和super关键字Apr 30, 2022 am 09:00 AM

本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

Java基础归纳之枚举Java基础归纳之枚举May 26, 2022 am 11:50 AM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

java中封装是什么java中封装是什么May 16, 2019 pm 06:08 PM

封装是一种信息隐藏技术,是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法;封装可以被认为是一个保护屏障,防止指定类的代码和数据被外部类定义的代码随机访问。封装可以通过关键字private,protected和public实现。

归纳整理JAVA装饰器模式(实例详解)归纳整理JAVA装饰器模式(实例详解)May 05, 2022 pm 06:48 PM

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于设计模式的相关问题,主要将装饰器模式的相关内容,指在不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式,希望对大家有帮助。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!