1. Processes and threads
process
Process: is a running program
- It is an independent unit for the system to allocate and call resources
- Each process has its own memory space and system resources
thread
Thread: it is a single sequential control flow in a process and an execution path
- Single thread: if a process has only one execution path, it is called a single threaded program
- Multithreading: if a process has multiple execution paths, it is called a multithreaded program
give an example
- Notepad program
- Mine clearance procedure
2. Implement multithreading by inheriting Thread class
Method 1: inherit Thread class
- Define a class MyThread to inherit the Thread class
- Override the run() method in the MyThread class
- Create an object of the MyThread class
- Start thread
Two small problems:
- Why override the run() method?
- Because run() is used to encapsulate the code executed by the thread
- What is the difference between the run() method and the start() method?
- run(): encapsulates the code executed by the thread. It is called directly, which is equivalent to the call of ordinary methods
- start(): start the thread; The run() method of this thread is then called by the JVM
package com.study_01; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(i); } } }
package com.study_01; public class MyThreadDemo { public static void main(String[] args) { MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); // my1.run(); // my2.run(); // void start() causes this thread to start execution; The Java virtual machine calls the run method of this thread. my1.start(); my2.start(); } }
3. Set and get thread name
Method for setting and getting Thread name in Thread class
- void setName (String name): change the name of this thread to be equal to the parameter name
- String getName(): returns the name of this thread
- ·The thread name can also be set through the construction method
How to get the thread name where the main() method is located?
- public static Thread currentThread(): returns a reference to the currently executing thread object
package com.study_02; public class MyThread extends Thread{ public MyThread() { } public MyThread(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } } /* public Thread() { this(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target) { this(group, target, "Thread-" + nextThreadNum(), 0); } private static int threadInitNumber; // 0 private static synchronized int nextThreadNum() { return threadInitNumber++; } */
package com.study_02; public class MyThreadDemo { public static void main(String[] args) { MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); // // //Thread renaming // my1.setName("high speed rail"); // my2.setName("aircraft"); // Thread(String name) // MyThread my1 = new MyThread("high speed rail"); // MyThread my2 = new MyThread("aircraft"); // // my1.start(); // my2.start(); // static Thread currentThread() returns a reference to the thread object currently executing. System.out.println(Thread.currentThread().getName()); } }
4. Thread priority
Threads have two scheduling models
- Time sharing scheduling model: all threads use the right to use the CPU in turn, and evenly allocate the time slice of CPU occupied by each thread
- Preemptive scheduling model: give priority to the threads with high priority to use CPU. If the threads have the same priority, one will be selected randomly. The threads with high priority will obtain more CPU time slices
If the computer has only one CPU, then the CPU can only execute one instruction at a certain time. Only when the thread gets the CPU time slice, that is, the right to use, can it execute the instruction. Therefore, the execution of multithreaded programs is random, because it is not certain who grabs the right to use the CPU
Method for setting and obtaining Thread priority in Thread class
- public final int getPriority(): returns the priority of this thread
- Public final void setpriority (int newPriority): change the priority of this thread
The default priority of thread is 5; The range of thread priority is: 1-10. High thread priority only means that the thread has a high probability of obtaining CPU time slice, but you can see the effect you want when it is run more times or more times
package com.study_03; public class ThreadPriority extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+":"+i); } } }
package com.study_03; public class ThreadPriorityDemo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("high-speed rail"); tp2.setName("aircraft"); tp3.setName("automobile"); // Get thread priority // System.out.println(tp1.getPriority()); // 5 // System.out.println(tp2.getPriority()); // 5 // System.out.println(tp3.getPriority()); // 5 // Change thread priority // tp1.setPriority(10000); // IllegalArgumentException // System.out.println(ThreadPriority.MAX_PRIORITY); // 10 // System.out.println(ThreadPriority.MIN_PRIORITY); // 1 // System.out.println(ThreadPriority.NORM_PRIORITY); // 5 // Set the correct priority tp1.setPriority(5); tp2.setPriority(10); tp3.setPriority(1); tp1.start(); tp2.start(); tp3.start(); } }
5. Thread control
6. Thread life cycle
7. Implement Runnable interface
Method 2: implement Runnable interface
- Define a class MyRunnable to implement the Runnable interface
- Override the run() method in the MyRunnable class
- Create an object of the MyRunnable class
- Create an object of Thread class and take the MyRunnable object as the parameter of the construction method
- Start thread
There are two implementation schemes of multithreading
- Inherit Thread class
- Implement Runnable interface
Advantages of implementing the Runnable interface over inheriting the Thread class
- It avoids the limitation of Java single inheritance
- It is suitable for multiple codes of the same program to deal with the same resource, and effectively separates the thread from the program code and data, which better reflects the object-oriented design idea
package com.study_05; public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
package com.study_05; public class MyRunnableDemo { public static void main(String[] args) { // Create an object of the MyRunnable class MyRunnable my = new MyRunnable(); // Create an object of Thread class and take the MyRunnable object as the parameter of the construction method // Thread(Runnable target) assigns a new thread object. // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); // Thread(Runnable target, String name) Thread t1 = new Thread(my,"high-speed rail"); Thread t2 = new Thread(my,"aircraft"); //Start thread t1.start(); t2.start(); } }
8. Selling tickets
Thread synchronization
package com.study_06; public class SellTicket implements Runnable { private int tickets = 100; @Override public void run() { while (true) { if (tickets > 0) { // Simulate the ticket selling time through the Sleep() method class try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; } } } }
package com.study_06; public class SellTicketDemo { public static void main(String[] args) { // Create an object of SellTicket class SellTicket st = new SellTicket(); // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name Thread t1 = new Thread(st,"Window 1"); Thread t2 = new Thread(st,"Window 2"); Thread t3 = new Thread(st,"Window 3"); // Start thread t1.start(); t2.start(); t3.start(); } }
9. Reflections on ticket selling cases
10. Synchronize code blocks to solve data security problems
Why is there a problem? (this is also the standard for us to judge whether there will be data security problems in multithreaded programs
- Is it a multithreaded environment
- Is there shared data
- Are there multiple statements that operate on shared data
How to solve the problem of multithreading security?
- Basic idea: let the program have no security environment
How?
- Lock the code for multiple statements to operate the shared data, so that only one thread can execute at any time
- Java provides a way to synchronize code blocks to solve k
Synchronous code block
Locking multiple statements to operate on shared data can be implemented using synchronous code blocks
- Format:
synchronized(Any object){ Code for multiple statements to operate on shared data }
- synchronized (arbitrary object): it is equivalent to locking the code, and any object can be regarded as a lock
Advantages and disadvantages of synchronization
- Benefits: it solves the data security problem of multithreading
- Disadvantages: when there are many threads, each thread will judge the lock on synchronization, which is very resource-consuming and will virtually reduce the running efficiency of the program
package com.study_08; public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { // Simulate the ticket selling time through the Sleep() method class try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; } } } } }
package com.study_08; public class SellTicketDemo { public static void main(String[] args) { // Create an object of SellTicket class SellTicket st = new SellTicket(); // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name Thread t1 = new Thread(st,"Window 1"); Thread t2 = new Thread(st,"Window 2"); Thread t3 = new Thread(st,"Window 3"); // Start thread t1.start(); t2.start(); t3.start(); } }
11. The synchronization method solves the problem of data security
Synchronization method: add the synchronized keyword to the method
- Format:
- Modifier synchronized return value type method name (method parameter) {}
What is the lock object of the synchronization method?
- this
Synchronous static method: add the synchronized keyword to the static method
- Format:
- Modifier static synchronized return value type method name (method parameter) {}
What is the lock object for synchronous static methods?
- Class name class
package com.study_09; public class SellTicket implements Runnable { // private int tickets = 100; private static int tickets = 100; private Object obj = new Object(); private int x = 0; @Override public void run() { while (true) { if (x % 2 == 0) { // synchronized (obj) { // synchronized (this) { synchronized (SellTicket.class) { if (tickets > 0) { // Simulate the ticket selling time through the Sleep() method class try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; } } } else { // synchronized (obj) { // if (tickets > 0) { // //Simulate the ticket selling time through the Sleep() method class // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket"); // tickets--; // } // } SellTicket(); } x++; } } // public void SellTicket() { // synchronized (obj) { // if (tickets > 0) { // //Simulate the ticket selling time through the Sleep() method class // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket"); // tickets--; // } // } // } // Lock on method // public synchronized void SellTicket() { // if (tickets > 0) { // //Simulate the ticket selling time through the Sleep() method class // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "selling" + tickets + "ticket"); // tickets--; // } // } public static synchronized void SellTicket() { if (tickets > 0) { // Simulate the ticket selling time through the Sleep() method class try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; } } }
package com.study_09; public class SellTicketDemo { public static void main(String[] args) { // Create an object of SellTicket class SellTicket st = new SellTicket(); // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name Thread t1 = new Thread(st,"Window 1"); Thread t2 = new Thread(st,"Window 2"); Thread t3 = new Thread(st,"Window 3"); // Start thread t1.start(); t2.start(); t3.start(); } }
12. Thread safe classes
StringBuffer
- Thread safe, variable character sequence
- Starting from version JDK5, it is replaced by StringBuilder. The StringBuilder class should usually be used because it supports all the same operations, but it is faster because it does not perform synchronization
Vector
- From Java 2 platform v1 2, this class improved the List interface to become a member of the Java Collections Framework. Unlike the new collection implementation, the Vector is synchronized. If a thread safe implementation is not required, it is recommended to use ArrayList instead of Vector
Hashtable
- This class implements a hash table that maps keys to values. Any non nul object can be used as a key or value
- From Java 2 platform v1 2, the class was improved to implement the Map interface and make it a member of the Java Collections Framework.
Unlike the new collection implementation, hashtables are synchronized. If thread safe implementation is not required, it is recommended to use HashMap instead of Hashtable
package com.study_10; import java.util.*; public class ThreadDemo { public static void main(String[] args) { StringBuffer sb = new StringBuffer(); StringBuilder sb1 = new StringBuilder(); Vector<String> v = new Vector<>(); ArrayList<String> arrayList = new ArrayList<>(); Hashtable<String ,String> ht = new Hashtable<>(); HashMap<String,String> hm = new HashMap<>(); // static <T> List<T> synchronizedList(List<T> list) // Returns a list of synchronization (thread safe) supported by the specified list List<String> list = Collections.synchronizedList(new ArrayList<String>()); } }
13.Lock lock
Although we can understand the lock object problem of synchronization code blocks and synchronization methods, we do not directly see where the lock is added and where the lock is released for clearer expression
How to add and release locks? JDK5 provides a new Lock object Lock
The Lock implementation provides a wider range of locking operations than using synchronized methods and statements
Lock provides methods for obtaining and releasing locks
- void lock(): get lock
- void unlock(): release the lock
Lock is an interface that cannot be instantiated directly. Its implementation class ReentrantLock is used to instantiate it here
Construction method of ReentrantLock
- Reentrantlock(): create an instance of ReentrantLock
package com.study_11; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTicket implements Runnable { private int tickets = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); if (tickets > 0) { // Simulate the ticket selling time through the Sleep() method class try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket"); tickets--; } } finally { lock.unlock(); } } } }
package com.study_11; public class SellTicketDemo { public static void main(String[] args) { // Create an object of SellTicket class SellTicket st = new SellTicket(); // Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name Thread t1 = new Thread(st,"Window 1"); Thread t2 = new Thread(st,"Window 2"); Thread t3 = new Thread(st,"Window 3"); // Start thread t1.start(); t2.start(); t3.start(); } }
14. Producer consumer model
Producer consumer model is a very classic multi-threaded cooperation model. Understanding the producer consumer problem can make us have a deeper understanding of multi-threaded programming
In fact, there are two types of threads:
- One is the producer thread used to produce data
- One is consumer thread, which is used to consume data
In order to decouple the relationship between producers and consumers, shared data areas are usually used, just like a warehouse
- The producer's production data is directly placed in the shared data area, and does not need to care about the behavior of consumers
- Consumers only need to obtain data from the shared data area, and do not need to care about the behavior of producers
In order to reflect the wait and wake-up during production and consumption, Java provides several methods for us to use. These methods are the wait and wake-up methods of Object class in Objec class:
15. Producer consumer cases
package com.study_12; public class Box { // Define milk quantity private int milk; // Define a member variable to represent the status of the milk box private boolean state = false; // Provides the operation of storing milk public synchronized void put(int milk) { // If there is milk, wait for consumption if (state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // If not, produce milk this.milk = milk; System.out.println("The milkman will"+this.milk+"Put a bottle of milk into the milk box"); // After production, modify the milk box status state = true; // awaken notifyAll(); } public synchronized void get() { // If not, wait for production if(!state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // Consume if you have System.out.println("The user gets the second"+this.milk+"Bottle milk"); state = false; // awaken notifyAll(); } }
package com.study_12; public class Customer implements Runnable{ private Box b; public Customer(Box b) { this.b = b; } @Override public void run() { while (true){ b.get(); } } }
package com.study_12; public class Producer implements Runnable{ private Box b; public Producer(Box b) { this.b = b; } @Override public void run() { for (int i = 1; i <= 5; i++) { b.put(i); } } }
package com.study_12; public class BoxDemo { public static void main(String[] args) { // Create a milk box formation, which is a shared data area Box b = new Box(); // Create a producer object and pass the milk box object as a constructor parameter, because the operation to store milk is called in this class Producer p = new Producer(b); // Create a consumer object and pass the milk box object as a constructor parameter, because the operation to store milk is called in this class Customer c = new Customer(b); // Create two thread objects Thread t1 = new Thread(p); Thread t2 = new Thread(c); // Start thread t1.start(); t2.start(); } }