• 周六. 10 月 12th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Java Concurrent Programming: three ways to create threads in Java

King Wang

1 月 3, 2022

List of articles

    • introduction
    • There are three ways to create a thread
    • One 、 Inherit Thread class
    • Two 、 Realization Runnable Interface
    • 3、 … and 、 Use Callable and Future Create thread
    • Three ways to compare

introduction

In daily development work , Multi-threaded development can be said to be a necessary skill , A good programmer must have an in-depth understanding of threads , I am a Java The programmer , also Java The language itself has very sophisticated support for thread development , So today we’re going to walk in the door , Learn to look Java How to create threads .

There are three ways to create a thread

Java There are three main ways to create threads :

1、 Inherit Thread class

2、 Realization Runnable Interface

3、 Use Callable and Future Create thread

The following sections discuss how each of these methods is implemented , And the contrast between them .

One 、 Inherit Thread class

step :

1、 Creates a thread subclass inheritance Thread class

2、 rewrite run() Method , Put in programs that require threads to execute run Method , The program in the method runs after the thread starts

2、 Create an instance of this class , And call the object’s start() Method to start the thread

The sample code is as follows :

public class ThreadDemo extends Thread{
@Override
public void run() {
super.run();
System.out.println(" The program that needs to be run ........");
}
public static void main(String[] args) {
Thread thread = new ThreadDemo();
thread.start();
}
}

When running main After the method , The program will execute run() The contents of the method , After execution , Threads die with them , Why do I have to rewrite it run() Methods? ?

After clicking the source of the method , Find out Thread Of run() The method doesn’t actually do anything, right

public void run() {
if (target != null) {
target.run();
}
}
public abstract void run();

If run() There is no program to run , Then the thread just dies when it starts . You have to override the thread to do something, right run() Method . meanwhile , The other thing to note is that , Thread startup requires a call start() Method , But call directly run() Methods can also be compiled , It works :

public static void main(String[] args) {
Thread thread = new ThreadDemo();
thread.run();
}

It’s just a normal method call , There is no new thread started , It loses the meaning of the thread itself .

Two 、 Realization Runnable Interface

1、 Defines a thread class implementation Runnable Interface , Override the interface run() Method , The method is still the one that contains the specified execution .

2、 Create a Runnable Implementation class instance , Regard it as target Parameters of the incoming , And create Thread Class instance .

3、 call Thread Class instance start() Method to start the thread .

public class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println(" I am a Runnable Interface ......");
}
public static void main(String[] args) {
RunnableDemo demo = new RunnableDemo();
Thread thread = new Thread(demo);
thread.start();
}
}

This is the interface based approach , Compared to inherit Thread Be much more flexible , But you need to create an additional thread object , Open source can be found , When put Runnable An instance of the implementation class as an argument target After the incoming , Assigned to the current thread class target, and run() The program that executes in is assigned to it target Of run() Method .

public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
........... Part of the source code is omitted here ..........
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
@Override
public void run() {
if (target != null) {
target.run();
}
}

3、 … and 、 Use Callable and Future Create thread

Use Callable Create threads and Runnable The interface way to create threads is similar , The difference is ,Callable The interface provides one call() Method as the body of the thread execution , and Runnable What the interface provides is run() Method , meanwhile ,call() Methods can have return values , And you need to use FutureTask Class to packaging Callable object .

public interface Callable<V> {
V call() throws Exception;
}

step :

1、 establish Callable Implementation class of interface , Realization call() Method

2、 establish Callable Implementation class instance , adopt FutureTask Class to packaging Callable object , The object is encapsulated Callable Object’s call() Return value of method .

3、 Will create the FutureTask Object as target Parameters of the incoming , establish Thread Thread instance and start a new thread .

4、 call FutureTask Object’s get Method gets the return value .

public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int i = 1;
return i;
}
public static void main(String[] args) {
CallableDemo demo = new CallableDemo();
FutureTask<Integer> task = new FutureTask<Integer>(demo);
new Thread(task).start();
try {
System.out.println("task The return value is :" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}

perform main After the method , The program outputs the following results :

task The return value is :1

explain ,task.get() Did return call() Result of method . So how does it work internally . To open the first FutureTask Construction method of , You can see that the interior is going to be Callable Object passed to the current instance as a parameter Callable member ,

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}

meanwhile , Member variable state Set as NEW, When to start task after , Its run Method will be executed Callable Of call() Method ,

public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// hold call() The return result is copied to result
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// Set the result to another variable
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// Assign the passed value to outcome member
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}

run() Method after a series of programs run , hold call() The return result is assigned to outcome, And then when called task.get() That’s what you get in the method outcome The value of the , thus , So we get the return result .

public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
// return outcome Value
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}

It can be seen that , The running logic of the source code is relatively clear , The code is also easier to understand , therefore , I’d rather suggest that readers take a look at it when they have time Java Underlying source code , This will give us a deeper understanding of how functionality is implemented .

Three ways to compare

Okay , All three ways to create an instance of a thread are covered , Here’s how they compare .

In terms of implementation , Use Runnable Interface and Callable The way the interface works is basically the same , It’s just that Callable The implemented method body can have a return value , And inheritance Thread Classes use inheritance , therefore , In fact, the three methods can be divided into two categories for analysis .

1、 Using inheritance Thread Kind of the way :

  • advantage : The coding is simple , also , When required to get the current thread , It can be used directly this
  • Inferiority : because Java Support single inheritance , So the inheritance Thread You cannot inherit from any other parent class

2、 Use Runnable Interface and Callable How to interface :

  • advantage :

More flexible , Threads simply implement the interface , You can also inherit from other parent classes .

In this way , Multiple threads can share one target object , It is ideal for multithreading the same resource .

Callable The interface also gets the return value .

  • Inferiority :

The code is a little bit more complicated , More objects need to be created .

If you want to access the current thread , Need to use Thread.currentThread() Method .

in general , Both categories have their advantages and disadvantages , But the latter’s comparative advantages are negligible , In general , It is also recommended that threads be created directly using interfaces , After all, the disadvantages of single inheritance are large .

发表回复