Java thread summary

Java thread summary (I)

1. Thread life cycle

1) Create (3 ways)

a. Inherit Thread class

public class ThreadDemo1 extends Thread{
		@Override
		public void run() {
			for(int i=0;i<10;i++) {
				System.out.println(i);
			}
		}
	}

b. Implement Runnable interface

public class ThreadDemo2 implements Runnable {
		@Override
		public void run() {
			for(int i=0;i<10;i++) {
				System.out.println(i);
			}
		}
	}

c. Implement Callable interface

public class ThreadDemo3 implements Callable<Integer>{
		@Override
		public Integer call() throws Exception {
			int s=0;
			for(int i=0;i<10;i++) {
				s+=i;
			}
			return s;
		}
		
	}

First, a and b need to rewrite the run () method without a return value, while c needs to rewrite the call method with a return value.

a is implemented by inheriting the Thread class, while b and c are implemented through interfaces. Through interfaces, it has strong scalability and can realize multiple interfaces to complete more complex functions.

2) Ready (. start() method)

public static void main(String[] args) {
		Thread t =new Thread();
		t.start();
	}

Create a thread object, call the start method to get ready, and then execute the run () method to start the thread

3) Run (. run() method)

4) Blocking (. block() method)

sleep()

public static class SleepThread extends Thread{
		@Override
		public void run() {
			for(int i=0;i<10;i++) {
				System.out.println(currentThread().getName()+":"+i);
				try {
					sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

Pause the currently executing thread and give other threads a chance to execute. You can also let threads with the same priority and high priority have the opportunity to execute.

yield()

public static class YieldThread extends Thread{
		@Override
		public void run() {
			for(int i=0;i<10;i++) {
				if(i==4) {
					Thread.yield();
				}
				System.out.println(currentThread().getName()+":"+i);
			}
		}
	}

This method is similar to sleep(), except that the user cannot specify how long to pause, and the yield () method can only give threads with the same priority the opportunity to execute.

join()

public static class JoinThread extends Thread{
		@Override
		public void run() {
			for(int i=0;i<10;i++) {
				
				System.out.println(currentThread().getName()+":"+i);
			}
		}
	}

The join() method completes the execution of the thread calling the method before that, that is, wait for the thread of the method to complete the execution before continuing to execute.

wait()

The wait() method suspends the execution of the current thread and releases the object lock flag, so that other threads can enter the synchronized data block, and the current thread is put into the object waiting pool

5) Death (natural death)

2.java thread memory model (CAS (ABA))

java thread includes a working memory. The working memory will read and save the variables in the main memory to the working memory for operation. It is private to each thread and cannot be accessed by other threads.

Workflow:

1. Copy from main memory to current memory

2. Execute code to change variables

3. Refresh relevant main memory with working memory (CAS: campare and swap)

Judge by comparing the values of main memory and working memory. If they are inconsistent, the data will be updated.

At this time, a new problem (ABA) will occur: first, the data a will be changed by means of B, and then it will be changed back to A. at this time, the data value has not changed, but the essence has changed.

Solution: add the version number to the data in the original main memory. After the operation, compare the version number first and then the value

3. Three characteristics of threads

Keyword volatile:

Volatile keyword is used to ensure memory visibility. When thread A updates the variable decorated with volatile, it will immediately flush to main memory and empty the value of the variable in other caches, so that other threads can only read the latest value from main memory. Variables modified with volatile keyword will get the latest data every time they are read. No matter which thread modifies this variable, it will be refreshed to the main memory immediately.

1) Visibility

When multiple threads access a variable at the same time, one thread modifies the value of the variable, and other threads can immediately see the modified value

public class ThreadSecurity {
	static  boolean flag=false;
	public static void main(String[] args) throws Exception {
		Thread t1=new ThreadA();
		Thread t2=new ThreadB();
		t1.start();
		Thread.sleep(1000);
		t2.start();

	}
	public static class ThreadA extends Thread{
		@Override
		public void run() {
			while(true) {
				if(flag) {
					System.out.println("A:"+flag);
					try {
						sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					break;
				}			
			}
		}
	}
	public static class ThreadB extends Thread{
		@Override
		public void run() {
			flag=true;
			
				System.out.println("B:"+flag);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {		
					e.printStackTrace();
				}
		}
	}
	
}

When the keyword volatile is not added, only thread B is started. Because thread A does not know that the flag becomes true, it still retains the original fasle, but adds the keyword before the flag. As A result, both threads are started to ensure the visibility of the flag.

2) Order

In the Java memory model, the compiler and processor are allowed to reorder instructions, but the reordering process will not affect the execution of single threaded programs, but the correctness of multi-threaded concurrent execution

Using the keyword volatile can solve the problem of ordering

3) Atomicity

It is similar to the atomicity of database transactions, that is, one or more operations are either executed completely and the execution process will not be interrupted by any factor, or they will not be executed at all. In JAVA, the reading and assignment operations of basic data type variables are atomic operations, that is, these operations can not be interrupted, either executed or not executed. That is, one or more operations are either fully executed without being interrupted by any factors, or they are not executed.

public class ThreadAtomicDemo {
	static volatile AtomicInteger x=new AtomicInteger(0);

	public static void main(String[] args) {
		Thread t1=new AtomicThread();
		Thread t2=new AtomicThread();
		Thread t3=new AtomicThread();
		
		t1.start();
		t2.start();
		t3.start();

	}
	
	public static class AtomicThread extends Thread{
		@Override
		public void run() {
			for(int i=0;i<100;i++) {
				System.out.println("current x:"+(x.incrementAndGet()));
				try {
					sleep(200);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
			}
		}
	}

}

If the Atomic package is not used, it will not reach 300 in the end. After use, the program can run normally to 300 to ensure thread safety

4. Four necessary conditions for deadlock

synchronized: also known as synchronous lock

a. When two threads access synchronized(this) code blocks in the same object concurrently, only one thread can be executed at a time. Another thread must wait for the current thread to execute the code block before executing the code

b. When one thread accesses the synchronized code block of an object, another thread can still access the non synchronized modified code block.

c. When a thread accesses synchronized code blocks, other threads' access to all other synchronized code blocks in the object is blocked.

d. Only one thread can acquire the lock of the object at the same time.

public synchronized test(){}//Locking method
public void test(){
  synchronized(this){}//Lock a sentence in the method
}

1) Mutually exclusive use

2) No preemption

3) Request and hold

4) Cycle waiting

Loop wait deadlock implementation:

public class DeadLockDemo {

	public static void main(String[] args) {
		DeadLock a = new DeadLock("A");
		DeadLock b = new DeadLock("B");
		DeadLock c = new DeadLock("C");
		DeadLockThread t1 = new DeadLockThread(a, b, c);
		DeadLockThread t2 = new DeadLockThread(b, c, a);
		DeadLockThread t3 = new DeadLockThread(c, a, b);
		t1.start();
		t2.start();
		t3.start();
	}

	public static class DeadLock {
		private String name;

		public DeadLock(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}

	}

	public static class DeadLockThread extends Thread {
		private DeadLock a;
		private DeadLock b;
		private DeadLock c;

		public DeadLockThread(DeadLock a, DeadLock b, DeadLock c) {
			this.a = a;
			this.b = b;
			this.c = c;
		}

		@Override
		public void run() {
			synchronized (a) {
				System.out.println("lock up" + a.getName() + "success");
				System.out.println("Ready to lock" + b.getName());
				synchronized (b) {
					System.out.println("lock up" + b.getName() + "success");
					System.out.println("Ready to lock" + c.getName());
					synchronized (c) {
						System.out.println("lock up" + c.getName() + "success");
					}
					System.out.println("release" + c.getName() + "success");
				}
				System.out.println("release" + b.getName() + "success");
			}
			System.out.println("release" + a.getName() + "success");
		}
	}

}

Finally, a deadlock is formed, and the program cannot be terminated.

Keywords: Java Multithreading

Added by nickman013 on Tue, 01 Feb 2022 12:51:33 +0200