JAVA network programming personal notes Chapter 3 multithreading

Posted by sandrine2411 on Sun, 05 Dec 2021 16:29:24 +0100

Multithreading

Multithreading, parallel, concurrent

  • Multithreading: it refers to that more than one thread is generated when the program (a process) runs
  • Parallelism: multiple cpu instances or multiple machines execute a section of processing logic at the same time, which is really simultaneous
  • Concurrency: through the cpu scheduling algorithm, users appear to execute at the same time. In fact, it is not really simultaneous from the perspective of cpu operation. Concurrency often has common resources in the scenario, so for this common resource, we will use TPS or QPS to reflect the processing capacity of the system

JAVA multithreading mechanism

What is the use of multiple processes

  • A single process computer does only one thing
  • Does a single core computer listen to music and play games at the same time?
  • The function of multi process is not to improve the running speed of CPU, but to improve the utilization of CPU
  • Memory between processes is independent

What is a thread

  • A thread is an execution scenario of a process. A process corresponds to multiple threads
  • Threads and threads share heap memory and method area memory, but have independent stack memory

What does multithreading do

  • Multithreaded computers can give people the illusion that multiple threads execute concurrently
  • Multithreading is not to improve execution speed, but to improve application utilization

Implementation method of multithreading

Two implementation methods:

  • Create Thread subclass
  • Generate a class to declare and implement the Runnable interface

Method for creating Thread subclass

Method implementation steps of creating Thread subclass

  1. Subclass of generated thread class: class MyThread extends Thread
  2. Override the run() method in a subclass: public void run()
  3. Generate the object of the subclass and call the start() method to start the new thread.
    MyThread thread = new MyThread();
    thread.start();
    //The start() method calls the run() method to execute the thread

Code example

Example of alternating two sub threads
class FirstThread extends Thread {
    public void run() {
        try {
            System.out.println("First thread starts running.");
            for (int i = 0; i < 16; i++) {
                System.out.println("First " + i);
                sleep(1000);
            }
            System.out.println("First thread finishes running.");
        } catch (InterruptedException e) {
        }
    }
}

class SecondThread extends Thread {
    public void run() {
        try {
            System.out.println("\tSecond thread starts running.");
            for (int i = 0; i < 16; i++) {
                System.out.println("\tSecond " + i);
                sleep(1000);
            }
            System.out.println("\tSecond thread finishes running");
        } catch (InterruptedException e) {
        }
    }
}

public class ExtendThread1 {
    public ExtendThread1() {
        FirstThread first = new FirstThread();
        SecondThread second = new SecondThread();
        first.start();
        second.start();
    }

    public static void main(String[] args) {
        // new ExtendThread1();
        FirstThread first = new FirstThread();
        SecondThread second = new SecondThread();
        first.start();
        second.start();
    }
}

Here is the standard method for creating Thread subclasses
class Thread extends Thread first
Then, in the above class, public void run()
Finally, the subclass object is generated in the main function, and the start() method is called to start a new thread

Main thread and sub thread examples
/*
 * 	One way to implement multithreading:
 * Step 1: inherit Java.lang.Thread
 * Step 2: rewrite the run method
 *  How do I create a thread? How do I start a thread?
 */
public class ExtendThread2 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // Create thread
        Thread t = new Processor();
        // Start thread
//		t.start();//  Code execution ends instantaneously, telling the JVM to allocate a new stack to the T thread
        // Run does not need to be called manually. After the system thread is started, the run method is called automatically.
        // If t.run() is used here; And?
        t.run();// This is an ordinary method call. In this way, the program has only one thread. After the run method ends, the following program can continue to execute
        // This code runs in the main thread
        for (int i = 0; i < 10; i++) {
            System.out.println("main:" + i);
//			sleep(1000);
        }
        /*
         * With multithreading, when the main method ends, there is no method stack frame in the main process stack. But there are stack frames in other threads or other stacks. The main method ends and the program may still be running.
         */
    }
}

//How to define threads
class Processor extends Thread {
    // Override run method
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("run:" + i);
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

The main thread and sub thread appear here. The main thread runs in the main function and the sub thread runs in the Processor defined by us

Here, if you use the start() method, the child thread and the main thread will run at the same time, and the effect is the same as that of the above two child threads.

Moreover, with multithreading, when the main method ends, there is no method stack frame in the main thread. But there are stack frames in other threads or other stacks. So the main method ends and the program may still be running

If you directly use the run() method to execute the thread, it is a normal method call. run() will be executed first, and then the thread in main() will be executed.

If the run() method and the start() method are called at the same time, the result is similar to that of the run() method

Create Thread subclass Thread summary

You need to deeply understand the run() and start() methods of the thread:

  • The start() method calls the run() method to execute the thread
  • The run() method is where the new thread does the specific work

Program entry: main() method
Thread entry: run() method
Applet entry: init() method

Method for implementing Runnable interface

Runnable is an interface in the java.lang package. It defines the function of creating and executing threads, and defines the run() method to complete specific tasks

Steps to implement the Runnable interface

  • Define a class to implement the Runnable interface, class FisrtThread implements Runnable {}
  • And implement the run () method in this class, public void run() {}
  • Generate the object of this class, FirstThread first = new FirstThread();
  • Use Thread(Runnable target) constructor to generate Thread object and then call start() to start the thread.
    Thread thread1 = new Thread(first);
    thread1.start();

Code example

Alternate instance of two sub threads
class FirstThread1 implements Runnable {
	public void run() {
		try {
			System.out.println("First thread starts running.");
			for (int i = 0; i < 6; i++) {
				System.out.println("First " + i);
				Thread.sleep(1000);
			}
			System.out.println("First thread finishes running.");
		} catch (InterruptedException e) {
		}
	}
}

class SecondThread1 implements Runnable {
	public void run() {
		try {
			System.out.println("\tSecond thread starts running.");
			for (int i = 0; i < 6; i++) {
				System.out.println("\tSecond " + i);
				Thread.sleep(1000);
			}
			System.out.println("\tSecond thread finished.");
		} catch (InterruptedException e) {
		}
	}
}

public class RunnableThread1 {
	public RunnableThread1() {
		FirstThread1 first = new FirstThread1();
		SecondThread1 second = new SecondThread1();
		Thread thread1 = new Thread(first);
		Thread thread2 = new Thread(second);
		thread1.start();
		thread2.start();
	}

	public static void main(String[] args) {
		new RunnableThread1();
	}
}

Similar to the thread interface used above, the runnable interface is used here
Here, a new class RunnableThread1() is created to run two sub threads

Main thread and sub thread instances
/*
 * 	One way to implement multithreading:
 * Step 1: implement Java.lang.Runnable
 * Step 2: implement the run method
 * 
 * How do I create a thread? How do I start a thread?
 */
public class RunnableThread2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// Create thread
		Thread t = new Thread(new Processor1());// Thread t = new Thread(Runnable);
		// Start thread
		t.start();// The execution of this code ends instantly and tells the JVM to allocate a new stack to the t thread
					// Run does not need to be called manually by the programmer. After the system thread is started, the run method is called automatically.
		// If t.run() is used here; And?
		t.run();// This is an ordinary method call. In this way, the program has only one thread. After the run method ends, the following program can continue to execute
		// This code runs in the main thread
		for (int i = 0; i < 100; i++) {
			System.out.println("main:" + i);
		}
		/*
		 * With multithreading, when the main method ends, there is no method stack frame in the main process stack. But there are stack frames in other threads or other stacks. The main method ends and the program may still be running.
		 */
	}
}

//How to define threads is recommended, because you can inherit classes while implementing interfaces
class Processor1 implements Runnable {
	// Override run method
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println("run:" + i);
		}
	}
}

It's similar to the above, but the results are different
After the start() method is started, it will run first, and then run() method. At this time, the start() method and run() method will rush to run. Only after they are all run can the following main thread be run.
In the thread interface, the start() method and run() method run alternately, one by one.

Comparison of two multithreading implementation methods

The same thing: you can implement multithreading

Difference: the Runnable interface is flexible
Application scope: subclass of Runnable Thread class

Multithreading status and scheduling

Thread state

newly build

  • When the object of a Thread class or its subclass is created, it enters this state
  • At this time, the thread object has been allocated memory space and its private data has been initialized, but the thread has not been scheduled. You can use the start() method to schedule or stop() method to terminate
  • Once the new thread is scheduled, it will switch to the executable state

Operational (executable)

  • In an executable environment, it can be scheduled at any time. It can be subdivided into two sub states:
    • Running status, CPU obtained, executing
    • Ready state, waiting only for processor resources
  • The transition of these two sub States is controlled by the execution scheduler

block

The state in which a thread is suspended for some reason

death

  • When a thread finishes executing or another thread calls the stop() method to stop it, it enters this stop state
  • It indicates that the thread has exited the runnable state and is no longer in the runnable state

Code example

/*
 *	Thread uses three methods:
 *1,Thread.currentThread();
 *2,t.getName()
 *3,t2.setName("t002");
 */
public class ThreadDemo {
	public static void main(String[] args) {
		// How do I get the current thread object?
		Thread t = Thread.currentThread();// t the thread pointed to by the saved memory address is the "main thread object".
		// How do I get the name of a thread?
		System.out.println(t.getName());
		Processor3 p = new Processor3();
		Thread t1 = new Thread(p);
		// 2. Name the thread
//		t1.setName("t001");
		t1.start();
		// 1. Create a thread in.
		Thread t2 = new Thread(p);
		// 2. Name the thread
//		t2.setName("t002");
		t2.start();
	}
}

class Processor3 implements Runnable {
	public void run() {
		Thread t = Thread.currentThread();// t the thread pointed to by the saved memory address is "t1 thread object".
		System.out.println(t.getName());
	}
}

The currentThread() method indicates that the thread pointed to by the saved memory address is a "t1 thread object".
The getName() method indicates: get the name of the thread
The setName() method indicates: set the name of the thread

If a thread gets a name without setting a name, the obtained name is: thread-x, and X is the number of threads without setting a name

Thread priority and scheduling

Q: why do threads need to be scheduled? Why introduce priority?

  • Multiple threads in the application can execute concurrently, but from the internal point of view of the system, all threads are still executed serially one by one. So how to decide which thread to execute first?
  • JAVA introduces the concept of priority. Priority is the priority of execution when a thread obtains the CPU. The higher the priority, the greater the power to obtain the CPU, the more opportunities for execution and the longer the execution time

thread priority

  • JAVA divides the priority into 10 levels, expressed as integers from 1 to 10. The higher the value, the higher the priority
  • Three priority constants are defined in the Thread class: MIN_PRIORITY,MAX_PRIORITY and NORM_PRIORITY, whose values are 1, 10 and 5 respectively
  • When assigning priority to a thread, its value should be between 1 and 10, otherwise an error will occur
  • If the application does not assign priority to the thread, the JAVA system assigns it NORM_PRIORITY
Code example
/*
 * Higher thread priority takes more time to obtain CPU time slice
 * 	Priority: 1-10
 * 	Maximum: 10
 * 	Default: 5
 */
public class ThreadPriorityDemo {
	public static void main(String[] args) {
		Thread t1 = new Processor5();
		t1.setName("t1");
		Thread t2 = new Processor5();
		t2.setName("t2");
		// 1. Show default priority
		System.out.println(t1.getPriority());// Default Priority 
		System.out.println(t2.getPriority());
		// 2. Set thread priority
		t2.setPriority(9);
		System.out.println(t2.getPriority());
		// Start thread
		t1.start();
		t2.start();
		// 3,MAX_PRIORITY--MIN_PRIORITY--NORM_PRIORITY
		System.out.println(Thread.MAX_PRIORITY);
		System.out.println(Thread.MIN_PRIORITY);
		System.out.println(Thread.NORM_PRIORITY);
	}
}

class Processor5 extends Thread {
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + "--------->" + i);
		}
	}
}

From the results, we can know that the priority of norm is 5, the maximum is 10 and the minimum is 1
Here, after the priority of thread2 is set to 9, the priority read by getPriority() method is 10. I don't know why

Thread scheduling principle

  • Scheduling is to allocate CPU resources and determine the execution order of threads
  • JAVA adopts preemptive scheduling mode, that is, high priority threads have the power to deprive low priority threads of execution
  • If a low priority thread is executing and a high priority thread appears, the low priority thread can only stop execution, abandon the CPU, return to the waiting queue, wait for the next round of execution, and let the high priority thread execute immediately
  • If threads have the same priority, they are scheduled according to the "first come, first serve" principle

If the high priority preempts the low priority thread and always occupies the CPU, how can the low priority thread obtain control?
Answer:

  1. Call the sleep() method to temporarily enter the sleep state, so as to give up the CPU, so that threads with the same priority and threads with low priority have the opportunity to execute
  2. Call yield() and give up the CPU, and the thread with the same priority will have the opportunity to execute
sleep() method
  • Format: Thread.sleep(ms);
  • Function: block the current thread, free up the CPU and transfer it to other threads
  • The sleep method is a static method
  • The interrupt method can be used
Code example
Use of sleep method
/*
	1.Thread.sleep(ms);
	2.sleep Method is a static method;
	3.The function of this method: block the current thread, free up the CPU and give it to other threads
 */
public class ThreadSleepDemo {
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new Processor06();
		t1.setName("t1");
		t1.start();
		//2. sleep to block the main thread
		for(int i=0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"--------->"+i);
			Thread.sleep(1000);
		}
	}
	
}
class Processor06 extends Thread{
	//The run method in Thread cannot throw exceptions, so after overriding the run method, throws cannot be used at the declaration position of the run method
	//Therefore, exceptions in the run method can only use try {...} catch() {}
	public void run() {//1. public void run() throws Exception cannot be used{
		for(int i=0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"--------->"+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

The sleep() method needs throws InterruptedException to get exceptions, but the run() method cannot throw exceptions, so try and catch are needed to get exceptions

sleep static method
/*
 * sleep Correct understanding of method as static method
 */
public class ThreadSleepDemo1 {
    public static void main(String[] args) throws Exception {
        // Create thread
        Thread t1 = new Processor7();
        t1.setName("t1");
        // Start thread
        t1.start();
        // dormancy
        // 1. What is blocked here is the current thread, which has nothing to do with thread t1. The sleep method is a static method, equivalent to Thread.sleep(5000);
        t1.sleep(5000);
        System.out.println("hello");
    }
}

class Processor7 extends Thread {
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println(Thread.currentThread().getName() + "--------->" + i);
        }
    }
}

sleep() is called in the main function, because it is static, so it has nothing to do with which thread. sleep is the current thread.

sleep is called with interrupt
/*
	A thread is sleeping. It will wake up after 5 seconds
 */
public class ThreadSleepDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Processor8();
        t1.setName("t1");
        t1.start();
        // Sleep for 5 seconds
        Thread.sleep(5000);
        // Abort sleep
        t1.interrupt(); // Suspend hibernation through exception handling mechanism
    }
}

class Processor8 extends Thread {
    public void run() {
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "------->" + i);
        }
    }
}

interrupt interrupts the sleep through the exception handling method. The following exception error will appear in the result

Better arousal methods
/*
	A thread is sleeping. Wake up the sleep after 5 seconds. Another better method
 */
public class ThreadSleepDemo3 {
    public static void main(String[] args) throws InterruptedException {

        Processor9 p1 = new Processor9();
        Thread t1 = new Thread(p1);
        t1.setName("t1");
        t1.start();
        //Sleep for 5 seconds
        Thread.sleep(5000);
        //Abort sleep
        p1.f = false;
    }
}
class Processor9 implements Runnable{
    public boolean f = true;

    public void run() {
        for(int i= 0; i < 9; i++ ) {
            if(f) {
                System.out.println(Thread.currentThread().getName()+"------->"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            else {
                return;
            }
        }
    }
}

Sleep is set to 1s in the run() method and 5s in the main function. After executing the run() method five times, sleep is aborted

yield method

Format: Thread.yield();
This method is a static method
Function: give way to threads with the same priority, but the time of giving way is not fixed

code implementation
/*
	Thread.yield();
	1.The method is a static method;
	2.Function: give way to threads with the same priority. However, the time of giving way is not fixed.
	
 */
public class ThreadYieldDemo {
	public static void main(String[] args) throws InterruptedException {

		Thread t1 = new Processor10();
		t1.setName("t1");
		t1.start();
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + "------->" + i);
		}
	}

}

class Processor10 extends Thread {

	public void run() {
		for (int i = 1; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + "------->" + i);
			if (i % 20 == 0)
				Thread.yield();
		}
	}
}

The results here show that t1 executed the yield() method after 20, but gave way at 28. After giving way, it basically didn't run much. It didn't start running a lot until the end of the main thread

Thread control

General framework of thread control

The Thread class defines many methods that control Thread execution

  • start(): used to call the run() method to start the thread execution
  • stop(): immediately stop thread execution, clear its internal state, and give up occupying resources
  • yield(): pause the scheduling thread and put it at the end of the waiting queue to wait for the next round of execution, so that other threads with the same priority have the opportunity to execute
  • suspend(): pauses the execution of the thread. All the state and resources of the thread remain unchanged. You can restart the thread later by calling the resume() method from another thread
  • resume(): resume suspended thread execution
  • sleep(): adjusts the JAVA execution time. The required parameter is the sleep time of the specified thread, in milliseconds
  • join(): the calling thread waits for the called thread to finish executing
  • isAlive(): determines whether the thread is currently executing
    • isAlive() returns false when the thread is in the "new" state
    • When a thread calls the start method and occupies CPU resources, the thread's run method starts running. Before the thread's run method ends, that is, before it enters the dead state, the thread calls the isAlive() method to return true
    • When the thread enters the "dead" state (the entity memory is released), the thread is still available to call isAlive. At this time, the returned value is false
Code example
/*
	Thread.join();
The calling thread waits for the execution of the called thread to end.
 */
public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Processor11();
        t1.setName("t1");
        t1.start();
        for (int i = 1; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "------->" + i);
            if (i % 20 == 0)
                t1.join();
        }
    }

}

class Processor11 extends Thread {

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "------->" + i);
//			if (i % 20 == 0)
//				Thread.yield();
        }
    }
}

At the beginning, the two threads compete with each other. When the main thread reaches 20, the join is triggered, and the execution will not continue until all the sub threads are executed

If you add yield to the child thread, the result remains the same. Because yield is re queued and does not end the process, you still need to wait until the child thread ends before running the main thread

Thread synchronization

Two threads t1 and t2

Asynchronous programming model: t1 Thread execution t1,t2 Thread execution t2 Yes, the two threads do their own work, and no one is waiting for anyone.
Synchronous programming model: t1 and t2 The execution of a thread is time sequential. One of them must wait for the completion of the other thread.

Why do I need synchronization?
For data security. Data synchronization reduces the utilization of applications, and thread synchronization mechanism makes programs (equivalent) single thread
When do I need thread synchronization?

  • Must be a multithreaded environment
  • A multithreaded environment shares one data
  • Shared data involves modification operations
/*
	Two threads t1 and t2

	Asynchronous programming model: t1 thread executes t1 and t2 thread executes t2. Each thread does its own thing, and no one is equal to the other.
	Synchronous programming model: the execution time sequence relationship between t1 and t2 threads, one of which must wait for the completion of the other thread.

	When do you want to synchronize?
		1.For data security. Thread synchronization reduces the utilization of applications, and thread synchronization mechanism makes programs (equivalent) single thread.
		2.Under what conditions do you want to use thread synchronization?
			1)Must be a multithreaded environment
			2)A multithreaded environment shares one data
			3)Shared data involves modification operations
 */
/*
 * The following program demonstrates an example of withdrawal. The following program uses multithreading but does not use thread synchronization mechanism
 * What problems will occur when you withdraw money from an account at the same time?
 */
public class ThreadAsynDemo {
    public static void main(String[] args) {
        // 3. Create a public account
        Account acc = new Account("62280013********", 5000.0);

        Processor12 p1 = new Processor12(acc);
        // 4. Create two threads
        Thread t1 = new Thread(p1);
        Thread t2 = new Thread(p1);
//5. Start two withdrawal operations
        t1.start();
        t2.start();

    }
}

//2. Withdrawal operation thread
class Processor12 implements Runnable {
    Account acc;

    Processor12(Account acc) {
        this.acc = acc;
    }

    public void run() {
        acc.withdraw(1000.0);
        System.out.println("Withdrawal 1000.0 Yuan success! Balance:" + acc.getBalance());
    }
}

//1. Accounts
class Account {
    private String accno;
    private double balance;

    public Account() {
    }

    public Account(String accno, double balance) {
        this.accno = accno;
        this.balance = balance;
    }

    // setter and getter
    public void setAccno(String accno) {
        this.accno = accno;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public String getAccno() {
        return accno;
    }

    public double getBalance() {
        return balance;
    }

    public void withdraw(double money) {
//		//Withdraw from current account
//		double after = this.balance - money;
//		//6. To ensure that something goes wrong, add hibernation
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		this.balance = after;
        synchronized (this) {
            // Withdraw from current account
            double after = this.balance - money;
            // 6. To ensure that something goes wrong, add hibernation
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.balance = after;
        }
    }
}

Synchronous lock

  • JAVA uses the means of lock flag to restrict the synchronization of the accessed data, so as to realize the protection of data
  • All protected resources are locked so that threads must obtain the lock flag to access the protected resources
  • Using synchronized

code implementation

/*
	The case problem of synchronized locking without modifier
 */
public class ThreadSynExam1 {
	public static void main(String[] args) throws InterruptedException {

		MyClass mc = new MyClass();
		Processor14 p = new Processor14(mc);
		Thread t1 = new Thread(p);
		t1.setName("t1");
		Thread t2 = new Thread(p);
		t2.setName("t2");
		// Start thread
		t1.start();
		Thread.sleep(1000);
		t2.start();
	}

}

class Processor14 implements Runnable {
	MyClass mc;

	Processor14(MyClass mc) {
		this.mc = mc;
	}

	public void run() {
		if (Thread.currentThread().getName() == "t1")
			mc.m1();
		if (Thread.currentThread().getName() == "t2")
			mc.m2();
	}
}

class MyClass {
	public synchronized void m1() {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("m1.........");
	}

	// The execution of m2 method does not need to wait for the end of m1, because there is no synchronized on m2 method
//	public void m2() {
//		System.out.println("m2.........");
//	}

	// The execution of m2 method needs to wait for the end of m1, because t1/t2 share the same object mc, and m2 methods are synchronized
	public synchronized void m2() {
		System.out.println("m2.........");
	}
}

During execution, m1 is started first, and the internal sleep of m1 lasts for 5s, and then m2 is started after 1s. m2 has a lock, so it needs to wait for m1 to be used up, so 1s here is also counted in 5s of m1. After 5s, m1 and m2 are completed successively

If m2 has no lock, m2 operation is completed 1s after startup and m1 is completed 5s after startup

Use of the modifier synchronized

  • In JAVA, the modifier synchronized is used to lock protected resources
  • synchronized can only be used to describe methods and code segments, not classes and member variables
  • Methods and code segments modified with synchronized are called method synchronization and code segment synchronization, which means that the method or code segment can only be executed by one thread at the same time, and other threads that want to execute the method or code segment must wait.
  • Method synchronization simply precedes the method with the synchronized modifier

Use of the modifier synchronized

  • In JAVA, the modifier synchronized is used to lock protected resources
  • synchronized can only be used to describe methods and code segments, not classes and member variables
  • Methods and code segments modified with synchronized are called method synchronization and code segment synchronization, which means that the method or code segment can only be executed by one thread at the same time, and other threads that want to execute the method or code segment must wait
  • Method synchronization simply precedes the method with the synchronized modifier

Synchronization is at the expense of CPU resources

  • The correct use of synchronization can reduce the mutual interference between threads and improve the stability and reliability of the program
  • Multiple threads in JAVA programs can interact through messages. Usually, you can wake up one or all other threads with notify() or notifyAll() methods
  • Use the wait() method to block the thread and wait for other threads to wake up with notify()
  • wait method and notify method are important parts of JAVA synchronization mechanism
  • Combined with the synchronized keyword, you can build many excellent synchronization models
  • Synchronization is divided into class level and object level, corresponding to class lock and object lock respectively
  • If the static method is modified by the synchronized keyword, the class lock must be obtained before another method is executed. Object locks are similar

code implementation

import java.lang.*;

class TestThread extends Thread{
    private int time = 0;
    public TestThread(Runnable r,String name){
        super(r,name);
    }
    public int getTime(){return time;}
    public int increaseTime(){return ++time;}
}
public class ThreadWaitNotify implements Runnable{
    public ThreadWaitNotify(){
        TestThread testthread1 = new TestThread(this,"1");
        TestThread testthread2 = new TestThread(this,"2");
        testthread2.start();
        testthread1.start();
    }

    public static void main(String[] args) {
        new ThreadWaitNotify();
    }
    public void run(){
        TestThread t = (TestThread) Thread.currentThread();
        try{
            if(!t.getName().equalsIgnoreCase("1")){
                synchronized (this){
                    wait();
                }
            }
            while(true){
                System.out.println("@time in thread"+t.getName()+"="+t.increaseTime());
                if(t.getTime()%3 == 0){
                    synchronized (this){
                        System.out.println("**********");
                        notify();
                        if(t.getTime()==30)break;
                        wait();
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

When entering the run() method,

Thread communication

  • Daemon application
    • When all other user threads end, the daemon thread exits
    • Daemon threads generally execute indefinitely
  • Data alternating operation application
    • Two data operate on the same data, involving notify and wait methods

Code example

Daemon thread

/*
 	Daemon thread
 	
 	When all other user threads end, the daemon thread exits!
 	Daemon threads generally execute indefinitely.
*/
public class ThreadAppDamon {
	public static void main(String[] args) throws Exception {
		Thread t1 = new Processors();
		t1.setName("t1");
		t1.setDaemon(true);
		t1.start();

		// Main thread
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "---------->" + i);
			 Thread.sleep(1000);
		}
	}
}

class Processors extends Thread {
	public void run() {
		int i = 0;
		while (true) {
			i++;
			System.out.println(Thread.currentThread().getName() + "---------->" + i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

Compared with normal threads, daemon threads are designed to run all the time
In addition, t1.setDaemon(true) should be included in the main function; This sentence

Data alternating operation

/*
 *	Two threads operate on the same data, involving notify and wait methods
 */
public class ThreadAppDataSharing {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Num num = new Num(0);
		Thread t1 = new Thread(new PrintOdd(num));
		Thread t2 = new Thread(new PrintEven(num));
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
}

//shared data 
class Num {
	int count;

	Num(int count) {
		this.count = count;
	}

	// Method of printing odd numbers
	/*
	 * t1 The thread executes this method, takes away the object lock of the num object, and outputs T1 ----- > 0 to wake up the t2 thread. Although the t2 thread is awakened,
	 * However, the t2 thread will not execute immediately because the t2 thread still cannot obtain the object lock. When the printadd method executes to this.wait(),
	 * t1 The thread waits indefinitely, the printadd method ends, and the object lock is released. The t2 thread obtains the object lock and starts executing the printEven method.
	 */
	public void printOdd() {
		// synchronized
		System.out.println(Thread.currentThread().getName() + "---------->" + count++);
//		this.notifyAll();
//		try {
//			this.wait();
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}

	public void printEven() {
		// synchronized
		System.out.println(Thread.currentThread().getName() + "---------->" + count++);
//		this.notifyAll();
//		try {
//			this.wait();
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}
}

// Thread 1
class PrintOdd implements Runnable {
	Num num;

	PrintOdd(Num num) {
		this.num = num;
	}

	public void run() {
		while (true) {
			num.printOdd();
		}
	}
}

//Thread 2
class PrintEven implements Runnable {
	Num num;

	PrintEven(Num num) {
		this.num = num;
	}

	public void run() {
		while (true) {
			num.printEven();
		}
	}
}

Concurrency framework

Parallel computing

  • Application background: there are many tasks and a large amount of data
  • Serial VS Parallel
    • Serial programming is simple and parallel programming is difficult
    • The frequency of a single computing core decreases, the number of computing cores increases, and the overall performance becomes higher
  • Parallel challenges
    • Task allocation and execution process are highly coupled
      • How to control granularity and cut tasks
      • How to assign tasks to threads and supervise the execution process of threads
  • Parallel mode
    • Master slave mode
    • Worker mode (worker worker)
  • JAVA Concurrent Programming
    • Thread/Runnable/Thread group management
    • Executor
    • Other frameworks

Thread group management

Thread group ThreadGroup

  • Collection of threads
  • In tree structure, large threads can include small thread groups
  • You can traverse the threads in the group through the enumerate method to perform operations
  • It can effectively manage multiple threads, but the management efficiency is low
  • Task allocation and execution process are highly coupled
  • Repeatedly create thread, close thread operation, unable to reuse thread

Executor framework

  • Starting from JDK5, the Executor framework is provided
    • Separate the creation of tasks and the creation of performers
    • Thread reuse (new threads are expensive)
  • Understand the concept of shared thread pool
    • Preset multiple threads to increase flexibility
    • Perform many small tasks many times
    • Task creation and execution process decoupling
    • Programmers do not need to care about the process of executing tasks in the thread pool

For the execution of the task, the execution strategy of the task shall be formulated
Each execution strategy must specify the execution details of many tasks, such as:

  • How many threads are used to perform the task
  • Do you create all the threads before processing the task, or do you create threads after encountering the task
  • Can the created thread be destroyed when it is idle
public interface Executor{
	void execute(Runnable command);
}

Executor, java defines different executor subclasses for different execution policies. The client programmer only needs to put the Runnable task into the execute method of the executor to indicate that the task has been submitted. After submission, it doesn't matter how these tasks are allocated or how threads execute

This decouples the submission and execution of tasks

Thread pool

Because the Executor subclass is responsible for executing tasks, it must contain some threads before these threads can be used to execute tasks. Therefore, the Executor subclass is also called thread pool, which means the pool containing threads
Generally, calling the execute method of the Executor will push the task into a queue, and then the thread in the thread pool will take the task from the queue for execution

The Executors class provides tools and methods (static methods) for creating and using thread pools in various scenarios

  • ExecutorService newFixdeThreadPool(int nThread) creates a thread pool with a fixed number of threads. The specific number is specified by the nThread parameter
  • ExecutorService newCachedThreadPool() creates a cacheable thread pool
  • ExecutorService newSingleThreadExecutor() creates a single threaded thread pool
  • ScheduledExecutorService newScheduledThreadPool(int corePoolSize) creates a thread pool with a fixed number of threads and executes tasks in a delayed or timed manner

Main category

The main classes are executorservice, ThreadPoolExecutor and future

  • Executors.newCachedThreadPool/newFixedThreadPool create thread pool
  • ExecutorService thread pool service
  • Callable specific logical object (thread class)
  • Future return results

Topics: Java network