Java multithreading operation

Four ways to create threads

Inherited from Thread class

  1. Create a subclass that inherits from the Thread class
  2. Override run() -- > of the Thread class to declare the operation performed by this Thread in run()
  3. An object that creates a subclass of the Thread class
  4. start() is called from this object
class Window extends Thread{
    private static int ticket = 100;
    @Override
    public void run() {

        while(true){

            if(ticket > 0){
                System.out.println(getName() + ": Ticket No.:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Implement Runnable interface

  1. Create a class that implements the Runnable interface
  2. The implementation class implements the abstract method in Runnable: run()
  3. Create an object that implements the class
  4. Pass this object as a parameter to the constructor of the Thread class to create an object of the Thread class
  5. start() is called through the object of the Thread class
class Window1 implements Runnable{

    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ":Ticket No.:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}

public class WindowTest1 {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Implement Callable interface

The Callable interface is more powerful than the Runnable interface

  1. call() can have a return value.
  2. call() can throw an exception, be caught by an external operation, and get the exception information
  3. Callable supports generics
//1. Create an implementation class that implements Callable
class NumThread implements Callable{
    //2. Implement the call method and declare the operation to be executed by this thread in call()
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {
        //3. Create an object of the Callable interface implementation class
        NumThread numThread = new NumThread();
        //4. Pass the object of this Callable interface implementation class to the FutureTask constructor as an object to create the FutureTask object
        FutureTask futureTask = new FutureTask(numThread);
        //5. Pass the FutureTask object as a parameter to the constructor of the Thread class, create the Thread object, and call start()
        new Thread(futureTask).start();

        try {
            //6. Get the return value of call method in Callable
            //The return value of get() is the return value of call() overridden by the FutureTask constructor parameter Callable implementation class.
            Object sum = futureTask.get();
            System.out.println("The sum is:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Thread pool

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. Provide a thread pool with a specified number of threads
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //Set the properties of the thread pool
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);    //  Size of core pool
//        service1.setKeepAliveTime();    // When a thread has no task, how long will it last at most and then terminate


        //2. Execute the operation of the specified thread. You need to provide an object that implements the Runnable interface or the Callable interface implementation class
        service.execute(new NumberThread());//Suitable for Runnable
        service.execute(new NumberThread1());//Suitable for Runnable

//        service.submit(Callable callable);// Suitable for callable
        //3. Close the connection pool
        service.shutdown();
    }
}

Thread is different from Runnable.

During development: priority: the way to implement Runnable interface
Reasons: 1. The implementation method does not have the limitation of single inheritance of classes. 2
2. The implementation method is more suitable to deal with the situation that multiple threads share data.
Contact: public class Thread implements Runnable
The same point: both methods need to override run(), and declare the logic to be executed by the thread in run().

Some methods commonly used in threads

  1. start(): start the current thread; Call the run() of the current thread
  2. run(): you usually need to override this method in the Thread class and declare the operation to be performed by the created Thread in this method
  3. currentThread(): a static method that returns the thread executing the current code
  4. getName(): get the name of the current thread
  5. setName(): sets the name of the current thread
  6. yield(): release the execution authority of the current cpu
  7. join(): join() in thread a calling thread b. At this point, thread a enters the blocking state, and thread a ends the blocking state until the thread b is fully executed.
  8. stop(): obsolete. When this method is executed, the current thread is forced to end.
  9. sleep(long millitime): lets the current thread "sleep" for the specified millitime milliseconds. The current thread is blocked for the specified millitime milliseconds.
  10. isAlive(): judge whether the current thread is alive

Thread safety

Synchronous code block

Synchronized (synchronized monitor){
//Code to be synchronized
}
The synchronization monitor can be any class, this pointer, or class. Class. Note that the third way is that all object instances share a monitor

class Window1 implements Runnable{

    private int ticket = 100;
//    Object obj = new Object();
    @Override
    public void run() {
        while(true){
            synchronized (this){//this: the only Window1 object / / method 2: synchronized (obj) / / method 3: synchronized (Windows.class){
                            //A thread safe code block is required
            }
        }
    }
}

Synchronization method

If the code that operates on shared data is completely declared in a method, we might as well synchronize this method declaration

  • When implementing Runnable, the synchronization monitor used by the common method is the this pointer
class Window3 implements Runnable {
    private int ticket = 100;

    @Override
    public void run() {
        while (true) {

            show();
        }
    }

    private synchronized void show(){//Sync monitor: this
        //Involving ticket logic code
    }
}
  • class name used by static method when inheriting thread mode
class Window4 extends Thread {
    private static int ticket = 100;

    @Override
    public void run() {

        while (true) {

            show();
        }

    }
    private static synchronized void show(){//Synchronization monitor: Window4.class
         //Involving ticket logic code
    }
}

lock

class Window implements Runnable{

    private int ticket = 100;
    //1. Instantiate ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{

                //2. Call the locking method lock()
                lock.lock();

                if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + ": Ticket No.:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally {
                //3. Call the unlock method: unlock()
                lock.unlock();
            }

        }
    }
}

public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}

Thread communication

Three methods involved:
wait(): once this method is executed, the current thread enters the blocking state and releases the synchronization monitor.
notify(): once this method is executed, one thread that is waiting will be awakened. If multiple threads are waiting, the one with higher priority will be awakened.
notifyAll(): once this method is executed, all threads that are wait ing will be awakened.

explain:
1. The three methods wait(), notify(), notifyAll() must be used in the synchronization code block or synchronization method.
2. The caller of the three methods wait(), notify(), notifyAll() must be the synchronization monitor in the synchronization code block or synchronization method. Otherwise, an IllegalMonitorStateException will appear
3. The three methods of wait(), notify(), notifyAll() are defined in the java.lang.Object class.

Producer consumer model

class Clerk{

    private int productCount = 0;
    //yield a product
    public synchronized void produceProduct() {

        if(productCount < 20){
            productCount++;
            System.out.println(Thread.currentThread().getName() + ":Start production" + productCount + "Products");

            notify();

        }else{
            //wait for
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
    //Consumer products
    public synchronized void consumeProduct() {
        if(productCount > 0){
            System.out.println(Thread.currentThread().getName() + ":Start consumption" + productCount + "Products");
            productCount--;

            notify();
        }else{
            //wait for
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

class Producer extends Thread{//producer

    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":Start production.....");

        while(true){

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            clerk.produceProduct();
        }

    }
}

class Consumer extends Thread{//consumer
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":Start consuming products.....");

        while(true){

            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            clerk.consumeProduct();
        }
    }
}

public class ProductTest {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer p1 = new Producer(clerk);
        p1.setName("Producer 1");

        Consumer c1 = new Consumer(clerk);
        c1.setName("Consumer 1");
        Consumer c2 = new Consumer(clerk);
        c2.setName("Consumer 2");

        p1.start();
        c1.start();
        c2.start();

    }
}

Tags: Java

Posted on Sun, 19 Sep 2021 16:41:23 -0400 by kiwi_uk