📜  了解生产者消费者问题上的线程Java

📅  最后修改于: 2021-04-25 01:01:10             🧑  作者: Mango

线程是执行的一部分,即它是程序中独立的执行路径。一个程序可以有多个线程,这引起了多线程的概念。为了使用线程执行特定任务,必须使用Java.lang.Thread类。在本文中,让我们看看通过程序实现线程的情况。

每当执行程序时,JVM都会首先检查“ Main”方法的存在。如果存在该方法,则默认情况下会创建一个线程,该线程称为“主线程”,因为它负责执行main方法中存在的语句。一个线程可以处于多种状态,本文将对此进行讨论。

有两种创建线程的方法。他们是:

  1. 通过为Thread类创建一个对象。
  2. 通过使用可运行接口。

通过扩展Thread类来创建线程:我们创建一个扩展Java.lang.Thread类的类。此类重写Thread类中提供的run()方法。线程在run()方法中开始其生命。我们创建线程类的对象,并调用start()方法开始执行线程。 Start()在线程对象上调用run()方法。让我们看一个使用胎面查找阶乘的示例:

// Java program to find the factorial
// of a number by the implementation
// of threads using thread class
  
class ThreadImplement extends Thread {
    static int fact = 1;
  
    // Overriding the run method
    // to find the factorial of
    // a number 5
    public void run()
    {
  
        // Loop to compute the factorial
        for (int i = 1; i <= 5; i++)
            fact = fact * i;
        System.out.println(fact);
    }
}
  
// Class to create a thread and
// compute the factorial
public class GFG {
    public static void main(String[] args)
    {
        // Creating an object of the
        // thread class
        Thread t1
            = new Thread(new ThreadImplement());
  
        // Computing the above class
        t1.start();
    }
}
输出:
120

通过实现Runnable接口创建线程线程类实现runnable接口,并且runnable接口仅包含run()方法。线程类中存在近37种方法,但我们通常使用22种方法。所有需要由线程执行的任务都必须放在run()方法内部,即,我们必须重写run()方法。为了启动线程,我们必须使用start()方法。启动线程后,该线程将执行run()方法中存在的语句。让我们使用可运行的接口实现相同的析因程序:

// Java program to find the factorial
// of a number by the implementation
// of threads using runnable interface
  
class ThreadImplement implements Runnable {
    static int fact = 1;
  
    // Overriding the run method
    // to find the factorial of
    // a number 5
    public void run()
    {
        // Loop to compute the factorial
        for (int i = 1; i <= 5; i++)
            fact = fact * i;
        System.out.println(fact);
    }
}
  
// Class to create a thread and
// compute the factorial
public class GFG {
    public static void main(String[] args)
    {
  
        // Creating an object of the
        // thread class
        Thread t1
            = new Thread(new ThreadImplement());
  
        // Computing the above class
        t1.start();
    }
}
输出:
120

Java的多线程:在计算中,生产者-消费者问题(也称为有界缓冲区问题)是多进程同步问题的经典示例。该问题描述了两个过程,即生产者和使用者,它们共享一个用作队列的固定大小的公用缓冲区。

  • 生产者的工作是生成数据,将其放入缓冲区中,然后重新开始。
  • 同时,使用者正在一次消费一个数据(即从缓冲区中删除数据)。

在这个问题中,我们需要两个线程,线程t1(产生数据)和线程t2(消耗数据)。但是,两个线程不应同时运行。

下面是生产者-消费者问题的实现:

// Java program to implement the
// producer consumer's problem
  
import java.lang.Thread;
  
// Producer class which extends the
// thread
class Producer extends Thread {
  
    // Creating a string buffer
    StringBuffer buffer;
    boolean dp = false;
  
    // Initializing the string buffer
    Producer()
    {
        buffer = new StringBuffer(4);
    }
  
    // Overriding the run method
    public void run()
    {
        synchronized (buffer)
        {
  
            // Adding the data into the
            // buffer
            for (int i = 0; i < 4; i++) {
                try {
                    buffer.append(i);
                    System.out.println("Produced " + i);
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
  
            // Notifying the buffer
            System.out.println("Buffer is FUll");
            buffer.notify();
        }
    }
}
  
// Consumer class which extends
// the thread
class Consumer extends Thread {
  
    // Creating the object of the
    // producer class
    Producer p;
  
    // Assigning the object of the
    // producer class
    Consumer(Producer temp)
    {
        p = temp;
    }
  
    // Overriding the run method
    public void run()
    {
  
        // Controlling the access of the
        // buffer to the shared producer
        synchronized (p.buffer)
        {
            try {
                p.buffer.wait();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
  
            // Printing the values of the string buffer
            // and consuming the buffer
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
            System.out.println("\nBuffer is Empty");
        }
    }
}
  
// Main class to implement the
// Producer Consumer problem
class GFG {
    public static void main(String args[])
    {
        // Initially, there needs to be some data
        // in order to consume the data. So,
        // Producer object is created first
        Producer p = new Producer();
  
        // Sending this producer object
        // into the consumer
        Consumer c = new Consumer(p);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
  
        // Since from the producer object,
        // we have already produced the data.
        // So, we start by consuming it.
        t2.start();
        t1.start();
    }
}
输出:
Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty

没有同步的生产者使用者问题:上面的代码效率低下,因为没有有效地利用CPU资源。线程正在等待处于等待状态的缓冲区。取而代之的是,我们可以通过终止并重新创建它们来有效地利用这些线程。那是:

  • 我们创建一个线程来产生数据。
  • 一旦缓冲区已满,我们将终止该线程。
  • 创建另一个线程来使用数据(此时生产者线程已死)。
  • 一旦缓冲区为空,使用者线程终止,创建生产者线程并生成数据(此时,使用者线程已死)。

下面是上述方法的实现:

// Java program to implement the
// producer consumer's problem
// without using synchronization
  
import java.lang.Thread;
  
// Producer class which extends the
// thread
class Producer extends Thread {
  
    // Creating a string buffer
    StringBuffer buffer;
  
    // Initializing the string buffer
    Producer()
    {
        buffer = new StringBuffer(4);
    }
  
    // Overriding the run method
    public void run()
    {
  
        // Loop to add data into the
        // buffer
        for (int i = 0; i < 4; i++) {
            try {
                buffer.append(i);
                System.out.println("Produced " + i);
            }
            catch (Exception e) {
  
                // Exception is returned when
                // the buffer is not accessible
                e.printStackTrace();
            }
        }
        System.out.println("Buffer is FUll");
  
        // Creating a consumer object after
        // execution of the above method.
        // Here, this keyword refers to
        // the current object of the
        // producer. This object is passed
        // into the consumer to access the
        // created buffer
        Consumer c = new Consumer(this);
    }
}
  
// Consumer class which extends
// the thread
class Consumer extends Thread {
    Producer p;
    Thread t2;
  
    // Constructor to get the
    // producer object
    Consumer(Producer temp)
    {
        p = temp;
  
        // Creating a new thread for
        // the object
        t2 = new Thread(this);
        t2.start();
    }
  
    // Overriding the run method
    public void run()
    {
        try {
  
            // Printing the string buffer and
            // consuming it
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\nBuffer is Empty");
    }
}
  
// Main class to implement the
// Producer Consumer problem
class Efficiency {
    public static void main(String args[])
    {
        // Producer object is created and
        // passed into the thread.
        Producer p = new Producer();
        Thread t1 = new Thread(p);
  
        // Here, instead of the same
        // thread waiting, a new thread
        // is created in the constructor
        // of the consumer class
        t1.start();
    }
}
输出:
Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty

修改后的生产者消费者问题:由于生产者和消费者正在使用相同的缓冲区,因此可以进一步改进上述方法。因此,与其使用多个线程,不如使用一个线程,这样一来,缓冲区最初是空的,而创建的线程充当了生产者。一旦缓冲区已满,该线程将充当使用者并使用数据。但是,我们需要避免死锁。

下面是上述方法的实现:

// Java program to implement the
// producer consumer's problem
// using single thread
import java.lang.Thread;
  
// Producer class which extends the
// thread
class Producer extends Thread {
  
    // Creating a string buffer
    StringBuffer buffer;
  
    // Variable to avoid the deadlock
    boolean dp = false;
    Thread t1;
    Producer()
    {
        // Initializing the buffer
        buffer = new StringBuffer(4);
  
        // Creating a new thread with
        // the current object
        t1 = new Thread(this);
        t1.start();
    }
  
    // Overriding the run method
    public void run()
    {
  
        // Loop to produce the
        // data and add it to
        // the buffer
        for (int i = 0; i < 4; i++) {
            try {
                buffer.append(i);
                System.out.println("Produced " + i);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("Buffer is FUll");
  
        // Creating a consumer object
        // by passing the current
        // producer object
        Consumer c = new Consumer(this);
  
        // Reinitializing the thread
        // with the new value of the
        // consumer object
        t1 = new Thread(c);
        t1.start();
    }
}
  
// Consumer class which extends
// the thread
class Consumer extends Thread {
    Producer p;
  
    // Constructor to initialize
    // with the producer object
    Consumer(Producer temp)
    {
        p = temp;
    }
  
    // Overriding the run method
    public void run()
    {
        try {
  
            // Loop to print the buffer and
            // consume the values
            for (int i = 0; i < 4; i++) {
                System.out.print(p.buffer.charAt(i) + " ");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\nBuffer is Empty");
    }
}
  
// Main class to implement the
// Producer Consumer problem
class GFG {
    public static void main(String args[])
    {
  
        // Creating the object of the
        // producer
        Producer p = new Producer();
    }
}
输出:
Produced 0
Produced 1
Produced 2
Produced 3
Buffer is FUll
0 1 2 3 
Buffer is Empty