multithreading
Published:
Java basics – multithreading
Multithreading
basic concepts
Concurrency and Parallelism
- Concurrency: multiple events occur in the same time period (alternate execution)
- Parallel: multiple events happen at the same time (simultaneous execution)
Processes and threads
- Process: an application running in memory
- Thread: an execution unit in the process, responsible for the execution of the program in the current process
Thread scheduling
- Time-sharing scheduling: All threads use the CPU in turn, and the time occupied by each thread is evenly distributed
- Preemptive scheduling: give priority to threads with high priority to use CPU, if the priority is the same, randomly select one
Main thread: the thread that executes the main method
Single-threaded program: There is only one thread in the program
Multithreading in Java
java.lang.Thread is a class that describes threads in Java
- Implementation steps:
- Create a subclass of Thread
- Override the run method in the Thread class in a subclass of the Thread class to set thread tasks
- Create a subclass object of the Thread class
- Call the start method in the Thread class, start a new thread, and execute the run method
Note: It is illegal to start a thread multiple times, especially after the current thread has been executed, it cannot be restarted
- Common methods
String getName()
: returns the name of the threadstatic Thread currentThread()
: Returns the reference of the currently executing threadvoid SetName(String name)
: change the thread nameThread(String name)
: Constructor, allocate a new thread objectstatic void sleep(long millis)
: make the currently executing thread pause at the specified millisecond value
Create thread method two
Implement Runnable interface
- Create a Runnable interface implementation class
- Override the run method in the implementation class and set thread tasks
- Create an implementation class object
- Create a Thread implementation class object, and pass the implementation class object of the Runnable interface in the constructor
- Call the start method in the Thread class to start a new thread and execute the run method
- Realize the benefits of the Runnable interface to create multiple threads
- Avoid the limitations of single inheritance
- Enhance the scalability of the program and reduce the coupling of the program (set thread tasks and start threads)
Anonymous inner class implements thread creation
Thread method
new Thread(){ @Override public void run(){ //... } }.start();
Runnable way
new Thread(new Runnable(){ @Override public void run(){ //... } }).start();
Thread safety
Multi-threaded access to shared data will cause thread safety issues
Thread synchronization
Thread safety issues can be solved through thread synchronization technology
- Synchronize code blocks
- Synchronization method
- Locking mechanism
Sync code block
The synchronized
keyword can be used in a block in the method, which means that only mutually exclusive access to the resources of this block is performed
synchronized (synchronization lock) {
Code that needs to be synchronized
}
Sync method
Synchronous methods ensure that only one thread can execute the method at the same time
public synchronized void method(){
Code that needs to be synchronized
}
The lock object of the synchronization method is this
Note: static synchronization method
public static synchronized void method(){
Code that needs to be synchronized
}
The lock object of the static synchronization method is the class attribute of this class -> class file object
Lock
java.util.concurrent.Locks.Lock interface
Lock implementation provides a wider range of locking operations than using synchronized methods and statements
- Methods in the Lock interface
void lock()
: Acquire a lockvoid unlock()
: release the lock
java.util.concurrent.Locks.ReentrantLock implements the Lock interface
Steps for usage:
- Create a ReentrantLock object in the member location
- Call lock to obtain the lock before the code that may have thread safety issues
- Call unlock to release the lock after the code that may have thread safety issues
Thread status
- Time WAITING
- BLOCKED
- WAITING
- NEW
- BLOCKED
- RUNNABLE
- TERMINATED
wait()
: The current thread is waitingnotify()
: wake up a single thread on the object monitorwait(long m)
: If the wait method is not awakened by notify after the end of the millisecond value, it will automatically wake up and the thread will enter the Runnable/Block statenotifyAll()
: wake up all threads on the object monitor
Waiting for the wake-up mechanism
Inter-thread communication
Concept: multiple threads are processing the same resource, but the processing actions are different
When multiple threads operate on the same data, it is necessary to avoid contention for the same shared variable
Wait and wake up mechanism
- Method:
wait()
: The thread is no longer active and enters the wait set without wasting CPU resources. At this time, the thread status is WAITING.notify()
: select a thread in the wait set of the notified object to releasenotifyAll()
: select all threads in the wait set of the notified object to release
Note:
- Even if only one waiting thread is notified, the notified thread cannot resume execution immediately (need to try again to acquire the lock)
- If the lock can be obtained, the thread changes from the WAITING state to the RUNNABLE state
- Otherwise, come out of the wait set and enter the entry set again, and the thread changes from the WAITING state to the BLOCKED state
- The wait and notify methods must be caled by the same lock object
- The wait and notify methods are methods belonging to the Object class, and objects of any class can be used
- The wait and notify methods must be used in synchronous code blocks or synchronous functions
Producer and consumer issues
The wait and wake mechanism deals with the issue of producer and consumer
We just make a buffer by ourselves, and the communication between the producer and the consumer passes through this buffer. A value of “” indicates that the buffer is empty, and a value other than “” indicates that the buffer is full:
public class ValueObject
{
public static String value = "";
}
Next is a producer, if the buffer is full, then wait(), no longer produce, and wait for the consumer to be notified when the consumer is finished; if the buffer is empty, then produce the data to the buffer
public class Producer
{
private Object lock;
public Producer(Object lock)
{
this.lock = lock;
}
public void setValue()
{
try
{
synchronized (lock)
{
if (!ValueObject.value.equals(""))
lock.wait();
String value = System.currentTimeMillis() + "_" + System.nanoTime();
System.out.println("The value of Set is:" + value);
ValueObject.value = value;
lock.notify();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
Consumers are similar, if the buffer is empty, then no more consumption, wait() waits, waiting for the producer to produce the notification; if the buffer is not empty, then go get the data:
public class Customer
{
private Object lock;
public Customer(Object lock)
{
this.lock = lock;
}
public void getValue()
{
try
{
synchronized (lock)
{
if (ValueObject.value.equals(""))
lock.wait();
System.out.println("The value of Get is:" + ValueObject.value);
ValueObject.value = "";
lock.notify();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
Write a main function, open two threads to call the getValue() method in Producer and the setValue() method in Customer():
public static void main(String[] args)
{
Object lock = new Object();
final Producer producer = new Producer(lock);
final Customer customer = new Customer(lock);
Runnable producerRunnable = new Runnable()
{
public void run()
{
while (true)
{
producer.setValue();
}
}
};
Runnable customerRunnable = new Runnable()
{
public void run()
{
while (true)
{
customer.getValue();
}
}
};
Thread producerThread = new Thread(producerRunnable);
Thread CustomerThread = new Thread(customerRunnable);
producerThread.start();
CustomerThread.start();
}
Thread Pool
Principle
Thread pool -> collection (LinkedList<Thread>
)
When the program is started for the first time, create multiple threads and save them in a collection
When you want to use a thread, you can remove the thread from the collection for use
After use, plan the thread to the thread pool
After JDK1.5+, a thread pool is built-in and can be used directly
Use
java.util.concurrent.Executors thread pool factory class, there are static methods to create thread pool
static ExecutorService newFixedThreadPool(int nThread)
: Create a reusable fixed thread thread pooljava.util.concurrent.ExecutorService thread pool interface
submit(Runnable task)
: Submit a Runnable task for execution, obtain threads from the thread pool, and execute thread tasksshutdown()
: Close and destroy thread pool To
Use of thread pool
- Use the static method newFixedThreadPool in the factory class of the Executors thread pool to produce a thread pool with a specified number of threads
- Create a class, implement the Runnable interface, rewrite the run method, and set thread tasks
- Call the submit method in ExecutorService, start the thread, and execute the run method
- Call the shutdown method in ExecutorService to destroy the thread pool (not recommended)