1, There are two ways to create threads
1. Inherit the Thread class and override the run method
- Direct code practice
public class Thread_ { public static void main(String[] args) { Cat cat = new Cat(); cat.start(); System.out.println("The main thread continues execution"+Thread.currentThread().getName());//Gets the name of the current thread //When the main thread starts a child thread, the main thread will not block and will continue to execute //At this time, the main thread and sub thread are alternately int i=0; while(i==30){ System.out.println("Main thread "+i++); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //When a class inherits the Thread class, it can be used as a Thread //We will rewrite the run method and write our own business code class Cat extends Thread { int time; @Override public void run() { while (true) { System.out.println("Today we learn threads" + time+++Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } if (time == 30) break; } } } ===========================================================================================
- When we run the program, we open the terminal and enter jconsole to open jconsole
- Find the program we just ran and connect
- - Then we can clearly see the main thread and Thread-0 thread in the thread
- When the corresponding code in the main thread runs, the main thread will also disappear
- When the Thread-0 thread runs, it does not disappear, because when it runs, the whole process ends and dies
From the above: in a thread, the main thread ends. If there are corresponding sub threads in progress, the whole process will not end
- Why don't we call the run method directly and use the start method that doesn't know where it comes from?
- We annotate the start method and directly call the run method for testing. We can see that instead of creating a new thread, it runs directly in the main method
- And it will wait until the run method is fully executed before proceeding
come to conclusion
- The run method is an ordinary method and does not really start a thread
- The start method creates a new thread to run the run method
- We annotate the start method and directly call the run method for testing. We can see that instead of creating a new thread, it runs directly in the main method
2. Implement the Runnable interface and rewrite the run method
- Because java inherits from a single class, in some cases, a class may have inherited a parent class. At this time, the method of inheriting Thread class can no longer be used to create threads
- At this point, we can create threads by implementing the Runnable interface
- Code practice
package com.java.xiancheng; public class Thread_Runnable_ { public static void main(String[] args) { Hpj hpj = new Hpj();//3. Create object - > 4 // hpj.start(); The start method cannot be called directly here. We can see from the source code that there is only one abstract method run in the Runnable interface //At this point, we can use the static proxy pattern in design pattern to create a Thread object and put the hpj object into the Thread Thread thread = new Thread(hpj);//4. Insert the created object method into Thread thread.start(); } } class Hpj implements Runnable {//1. First implement the interface - > 2 int counter = 0; @Override public void run() {//2. Rewrite run method - > 3 while (true) { System.out.println("Xiao Hou is learning thread today" + counter++ + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3. Find problems by writing simulated ticketing code
- The code is as follows:
package com.java.day6; public class MaiPiao { public static void main(String args[]) { Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket,"Window one"); Thread t2 = new Thread(ticket,"Window two"); Thread t3 = new Thread(ticket,"Window 3"); Thread t4 = new Thread(ticket,"Window 4"); t1.start(); t2.start(); t3.start(); t4.start(); } static class Ticket implements Runnable { private static int tickets = 50; public Ticket() { super(); } @Override public void run() { // TODO Auto-generated method stub while (true) { if (tickets <= 0) break; //tickets--; System.out.println(Thread.currentThread().getName()+"...This is the second" + tickets-- + "Ticket No"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
- After several tests, we will find that in the process of buying tickets, we will find that one ticket is sold more, and the order of tickets is also disordered
- When we change the condition for the end of the cycle to exit the cycle if the number of votes is equal to 0, there is even a case of selling negative tickets
- How can we solve this problem? We can solve this problem in the subsequent study. Please look down slowly
2, Methods commonly used in threads
1.yield and join
- yield: the thread is courteous. Give up the cpu and let other threads execute. However, the courteous time is uncertain and determined by the cpu, so it may not be successful
- join: thread queue jumping. Once the queue jumping thread is successful, it must first complete all the tasks of the inserted thread
- Code practice:
package com.java.xiancheng; public class Join_yield { public static void main(String[] args) { Hpj1 hpj = new Hpj1(); Thread h = new Thread(hpj); h.start(); for (int i = 1; i <= 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Xiaohou 2 learning thread" + i + "minute"); if (i == 5) { try { System.out.println("Xiao Hou 2 has been learning for five minutes. Let Xiao Hou learn first"); h.join();//This is equivalent to letting the h thread execute first and then come back to execute. The rest will be successful System.out.println("I'll learn when Xiao Hou finishes"); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class Hpj1 implements Runnable { int num = 1; @Override public void run() { while (num <= 10) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("The second part of Xiaohou learning thread" + num++ + "minute"); } } }
- From the running results, it can be seen that the join successfully allows the main thread to meet the conditions again, and then allows the h thread to run and then run back
- After we replaced the join with yield, the cpu thought I could handle it after a while, and then it returned to the original state
3, Status of the thread
Thread state | name | describe |
---|---|---|
new | newly build | If a thread has just been created and the start method has not been called, or the start method has just been called, calling the start method does not necessarily "immediately" change the thread state. It may take some steps to complete the start of a thread. |
RUNNABLE | Operable | When the start method call ends, the thread changes from NEW to RUNNABLE. The thread is alive and tries to preempt CPU resources, or has preempted CPU resources and is running. The status of both cases is displayed as RUNNABLE |
BLOCKED | Lock blocking | Both thread A and thread B execute method test, and method test is locked. Thread A obtains the lock to execute the test method first. Thread B needs to wait for thread A to release the lock. At this time, thread B handles BLOCKED |
WAITING | Wait indefinitely | When a thread is Waiting for another thread to perform a (wake-up) action, the thread enters the Waiting state. You cannot wake up automatically after entering this state. You must wait for another thread to call the notify or notifyAll method to wake up. |
TIMED_WAITING | Limited waiting | It is similar to the WAITING state, but there is a time limit. When the time comes, you will wake up on your own initiative |
TERMINATED | Termination (death) | The thread whose execution of the run method ends is in this state. |
4, Thread synchronization mechanism
1. Understanding of thread synchronization mechanism
- In multithreaded programming, some sensitive data are not allowed to be accessed by multiple threads at the same time. At this time, synchronous access technology is used to ensure that at most one thread can access the data at any time to ensure the integrity of the data
- It can also be understood that when a thread is operating on the memory, other threads cannot operate on the memory address until the thread completes the operation
2. How to realize thread synchronization
- Synchronous code block
synchronized(object){//Get the lock of the object to synchronize the code //Code to be synchronized } ```
- Synchronized can also be placed in the method declaration, indicating that the whole method is a synchronized method
public synchronized void hpj(String name){ //Code to be synchronized } ```
- Now we can solve the error in the above ticketing code
- Put the lock directly on the run method
public synchronized void run() { //Omitted here }
- After the program runs, we find that only one thread is buying tickets. This is because when the first thread grabs the lock, it will execute all the code in the run method, and then release the lock to let other threads run. When other threads grab the lock, the end condition of the run method has been established, and then the program ends
- So this method is not feasible
- It is feasible to add in the while loop
- Put the lock directly on the run method
- The synchronized keyword modifies a non static method. this is used as the lock object by default, and cannot be specified separately
- The synchronized keyword modifies a static method. By default, the Class object of the current Class is used as the lock object, and cannot be specified separately
3. Thread deadlock
- Example code:
public class ThreadDeadLock extends Thread{ private Object obj1; private Object obj2; public ThreadDeadLock(Object obj1,Object obj2) { this.obj1 = obj1; this.obj2 = obj2; } public void run() { String name = Thread.currentThread().getName(); if("Thread-0".equals(name)){ while(true){ synchronized (obj1) { synchronized (obj2) { System.out.println(name+" It's running.."); } } } } else{ while(true){ synchronized (obj2) { synchronized (obj1) { System.out.println(name+" It's running.."); } } } } } public static void main(String[] args) { Object obj1 = new Object(); Object obj2 = new Object(); Thread t1 = new ThreadDeadLock(obj1,obj2); Thread t2 = new ThreadDeadLock(obj1,obj2); t1.start(); t2.start(); } }
- Deadlock means holding the lock needed by each other