线程是执行的一部分,即它是程序中独立的执行路径。一个程序可以有多个线程,这引起了多线程的概念。为了使用线程执行特定任务,必须使用Java.lang.Thread类。在本文中,让我们看看通过程序实现线程的情况。
每当执行程序时,JVM都会首先检查“ Main”方法的存在。如果存在该方法,则默认情况下会创建一个线程,该线程称为“主线程”,因为它负责执行main方法中存在的语句。一个线程可以处于多种状态,本文将对此进行讨论。
有两种创建线程的方法。他们是:
- 通过为Thread类创建一个对象。
- 通过使用可运行接口。
通过扩展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