Several Common Ways to Create Multithreading

Multithreading

Four ways to create multithreading:

1.1 Inheritance of Thread Class

  • Advantages: Simple code
  • Disadvantage: Can't inherit other classes (Java single inheritance multiple implementations)
//Inheriting the Thread class
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        t1.setName("Thread 1:");
        t2.setName("Thread 2:");
                t1.start();
                t2.start();
    }
}
class MyThread extends Thread{
       int i=100;
    @Override
    public void run() {
        while (i>0){  
   System.out.println(Thread.currentThread().getName()+i--);
        }
    }
}

1.2 Implementation of runnable Interface

  • Advantages: Other classes can be inherited, and instances that implement the interface can share resources (member variables)
  • Disadvantage: Complex code
public class RunnableDemo {
    public static void main(String[] args) {
        RunableThread runableThread = new RunableThread();
        Thread thread1 = new Thread(runableThread,"Thread 1:");
        Thread thread2 = new Thread(runableThread,"Thread 2:");
        thread1.start();
        thread2.start();
    }
}
class RunableThread implements Runnable{
    int i;
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {   System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

1.3 Implement callable interface (wrapped by FuturTask class)

  • Advantages: The call() method that implements the interface inside has a return value.
    • Call the return value: By calling the get() method of FuturTask.
public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableThread callableThread = new CallableThread();
        FutureTask futureTask1 = new FutureTask(callableThread);
        FutureTask futureTask2 = new FutureTask(callableThread);
       Thread t1  = new Thread(futureTask1,"Thread 1");
        Thread t2  = new Thread(futureTask2,"Thread 2");
        t1.start();
        t2.start();
        System.out.println(futureTask1.get());
        System.out.println(futureTask2.get());
    }
}
class CallableThread implements Callable{
    int i=100;
    @Override
    public Object call() throws Exception {
        while (i>0){         System.out.println(Thread.currentThread().getName()+i--);
        }
        return Thread.currentThread().getName()+"Return value of thread,Print at the end of execution";
    }
}

1.4 Use Thread Pool

(The Executors class provides a series of factory methods to create thread pools, and the returned methods all implement the ExecutorService interface)

  • Advantages: Automated assembly, easy management and recycling of resources
  • Disadvantage: The Alibaba java development manual explicitly prohibits the use of Executors to create thread pools
    • Reason: Executors creates thread pools without the length of the incoming blocking queue. A blocking queue is a boundless queue. For a boundless queue, tasks can be added to it indefinitely. In this case, too many tasks may cause memory overflow.
    • Recommendation 1: Customize the ThreadPoolExecutor class to create threads, but cannot define thread names
    • It is strongly recommended to use Guava's ThreadFactoryBuilder to create thread pools that can set thread names.
//The method of creating thread pool in JDK is easy to OOM. Not recommended
public class ThreadPoolDemo {
    public static void main(String[] args) {
        //Create a specified number of thread pool objects
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new RunableThread2());
        pool.submit(new RunableThread2());
        //Close thread pool
        pool.shutdown();
    }
}
class RunableThread2 implements Runnable{
    int i=100;
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {    System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
//Why not create a thread pool with Executors and block the queue with the default size of Integer.Max_Value?
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
//Customize ThreadPool Executor and set the blocking queue size to 5
 ExecutorService pool = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5))
//Ali recommends using Guava's ThreadFactoryBuilder class to create
     //Setting Thread Name
     ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread%d:").build();
	//Create thread pools
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10), threadFactory, new ThreadPoolExecutor.AbortPolicy());

2 Lock Interface's Advantage over synchronized Code Block

  • The acquisition and release locks can be displayed, and the use of locks is more flexible.

    • Method in Lock:
      • lock(); lock
      • unlock(); unlock
  • Fair lock can be easily realized

    • Fair Lock: First come first served, first come first served; (Efficiency is not as high as unfair lock)
    • Unfair Lock: A preemptive mechanism for acquiring locks, which may not necessarily get locks in the first place, and may not be accessible by threads at all.
  • Lock is an extended version of synchronized. Lock provides unconditional, round-robin (tryLock method), timing (tryLock method with parameters), interruptible (lock Interruptibly), and multi-conditional queue (new Conditions method) lock operations. The implementation classes of Lock basically support fair locks and unfair locks (default), while synchronized only supports unfair locks.

Keywords: Java JDK

Added by Pointybeard on Wed, 11 Sep 2019 05:41:56 +0300