Java learning record (intermediate)-[5], multi-threaded

(1) There are 3 ways to create multiple threads, namely: ↓ ↓ ↓ 

             ① Inherit the thread class

             ② Implement Runnable interface

             ③ Anonymous class

// 继承线程类的方式
public class MultiThreadTest {

	public static void main(String[] args) {
		
        // 创建线程1
		MyThread thread1 = new MyThread();
        // 创建线程2
		MyThread thread2 = new MyThread();

		thread1.start();    // 启动线程1
		thread2.start();    // 启动线程2

	}

}

class MyThread extends Thread{
	
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("我是线程【" + this.getName() + "】");
		}
	}
}

running result:

// 实现Runnable接口的方式
public class MultiThreadTest {

	public static void main(String[] args) {

		MyRunnable runnable1 = new MyRunnable(1);
		MyRunnable runnable2 = new MyRunnable(2);
		new Thread(runnable1).start();
		new Thread(runnable2).start();

	}

}

class MyRunnable implements Runnable {

	int a = 0;

	public MyRunnable(int a) {
		this.a = a;
	}

	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("我是线程【" + a + "】");
		}
	}

}

running result:

// 匿名类的方式
public class MultiThreadTest {

	public static void main(String[] args) {
		
		new Thread() {
			public void run() {
				for (int i = 0; i < 30; i++) {
					System.out.println("我是线程【" + this.getName() + "】");
				}
			};
		}.start();
		
		new Thread() {
			public void run() {
				for (int i = 0; i < 30; i++) {
					System.out.println("我是线程【" + this.getName() + "】");
				}
			};
		}.start();

	}

}

running result:

(2) Common thread methods

Method name Features
sleep Current thread paused
join Join the current thread (the current thread waits for the end of a thread to continue running)
setPriority Set thread priority
yield Temporary suspension
setDaemon Daemon thread

[Threads with high priority have a greater chance of obtaining CPU resources]

[The current thread is temporarily suspended, so that other threads can have more opportunities to occupy CPU resources]

[ The concept of daemon threads is: when all threads in a process are daemon threads, end the current process ]

[Daemon threads are usually used for logging, performance statistics, etc.]

(3) The synchronization problem of multithreading

Multi-threaded synchronization problems refer to problems that may be caused when multiple threads modify one data at the same time 

E.g:

There is 100,000 yuan in a certain bank card at home. On this day, the wife went out shopping, and at the same time, the husband’s salary will be applied to this card today;

When the husband’s salary came in, the balance of the bank card was 100,000, and the salary was 20,000, so the final balance in the bank card should be 120,000;

But at the same time, the wife buys clothes. At this time, the husband's salary has not yet been paid, and the balance is 100,000 yuan. The payment is 1,000 yuan, and there should be 99,000 yuan left;

As a result, a tragedy happened. Just a moment before, the husband's salary came, and the balance immediately became 99,000 as soon as it became 120,000.

It's 20,000 yuan less! ! !

[1], problem solving ideas

While the deposit thread is accessing the balance, other threads cannot access the balance

[2], synchronized synchronization object concept

Object someObject =new Object();
synchronized (someObject){
  //此处的代码只有占有了someObject后才可以执行
}

Synchronized means that the current thread, the exclusive object someObject, the
current thread monopolizes the object someObject, if another thread tries to occupy the object someObject, it will wait until the current thread releases its occupation of someObject.
someObject is also called a synchronization object. All objects can be used as synchronization objects.
In order to achieve the synchronization effect, the same synchronization object must be used

. The way to release the synchronization object: the synchronized block ends naturally, or an exception is thrown

[3], thread-safe class

If a class whose methods are all modified with synchronized, then the class is called a thread-safe class.

At the same time, only one thread can enter an instance of this class to modify the data, thereby ensuring the safety of the data in this instance (It will not be modified by multiple threads at the same time and become dirty data)

(4), thread-safe classes

HashMap 和 HashTable 的区别:

区别1: 
        HashMap    可以存放 null
        HashTable  不能存放 null
区别2:
        HashMap    不是线程安全的类
        HashTable  是线程安全的类
StringBuffer 和 StringBuilder 的区别:

StringBuffer  是线程安全的
StringBuilder 是非线程安全的

所以当进行大量字符串拼接操作的时候:
    如果是单线程,就用StringBuilder会更快些,
    如果是多线程,就需要用StringBuffer 保证数据的安全性

【非线程安全的为什么会比线程安全的 快? 因为不需要同步嘛,省略了些时间】
ArrayList 和 Vector 的区别:

ArrayList  不是线程安全的类
Vector     是线程安全的类

【其它一模一样】
把非线程安全的集合转换为线程安全:

借助 Collections.synchronizedList() , 可以把非线程安全的集合转换为线程安全的集合

(5), deadlock

[1], Demo deadlock

1. 线程1 首先占有对象1,接着试图占有对象2
2. 线程2 首先占有对象2,接着试图占有对象1
3. 线程1 等待线程2释放对象2
4. 与此同时,线程2等待线程1释放对象1
就会。。。一直等待下去,直到天荒地老,海枯石烂,山无棱 ,天地合。。。

public class DeadLockTest {

	public static void main(String[] args) {
		final Hero ahri = new Hero();
		ahri.name = "九尾妖狐";
		final Hero annie = new Hero();
		annie.name = "安妮";

		Thread t1 = new Thread() {
			public void run() {
				// 占有九尾妖狐
				synchronized (ahri) {
					System.out.println("t1 已占有九尾妖狐");
					try {
						// 停顿1000毫秒,另一个线程有足够的时间占有安妮
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					System.out.println("t1 试图占有安妮");
					System.out.println("t1 等待中 。。。。");
					synchronized (annie) {
						System.out.println("do something");
					}
				}

			}
		};
		t1.start();
		Thread t2 = new Thread() {
			public void run() {
				// 占有安妮
				synchronized (annie) {
					System.out.println("t2 已占有安妮");
					try {

						// 停顿1000秒,另一个线程有足够的时间占有暂用九尾妖狐
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("t2 试图占有九尾妖狐");
					System.out.println("t2 等待中 。。。。");
					synchronized (ahri) {
						System.out.println("do something");
					}
				}

			}
		};
		t2.start();
	}

}

class Hero {
	String name;
}

Results of the:

(6) Interaction between threads

There is a need for interactive notification between threads. Consider the following situation: 
There are two threads that handle the same hero. 
One increases blood, the other reduces blood. 

The blood-reducing thread finds that the blood volume=1, stop blood-reducing until the blood-increasing thread adds blood to the hero, then it can continue to reduce blood

[1], stupid way

The while loop is used in the blood reduction thread to determine whether it is 1. If it is 1, the loop will not stop until the blood increase thread restores the blood volume.
This is a bad solution because it will take up a lot of CPU and slow down the performance.

[2], use wait and notify for thread interaction

In the Hero class: hurt() blood loss method: when hp=1, execute this.wait().
this.wait() means that the thread that owns this waits, and temporarily releases
the thread that has entered the hurt method. The blood reduction thread, this.wait() will make the blood reduction thread temporarily release its possession of this. In this way, the blood adding thread has the opportunity to enter the recover() blood adding method.


Recover() method of adding blood: increase the blood volume, execute this.notify();
this.notify() means to notify those threads waiting in this that they can wake up. The thread waiting on this is precisely the blood-reducing thread. Once recover() ends, the blood adding thread releases this, and the blood reducing thread can reoccupy this and perform subsequent blood reduction work.

使ç¨waitånotifyè¿è¡çº¿ç¨äº¤äº

public class InteractionTest {

	public static void main(String[] args) {

		final LOLHero gareen = new LOLHero();
		gareen.name = "盖伦";
		gareen.hp = 616;

		Thread t1 = new Thread() {
			public void run() {
				while (true) {

					gareen.hurt();

					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}
		};
		t1.start();

		Thread t2 = new Thread() {
			public void run() {
				while (true) {
					gareen.recover();

					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}
		};
		t2.start();

	}

}

class LOLHero {
	public String name;
	public float hp;

	public int damage;

	public synchronized void recover() {
		hp = hp + 1;
		System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
		// 通知那些等待在this对象上的线程,可以醒过来了,如等待着的减血线程,苏醒过来
		this.notify();    //
	}

	public synchronized void hurt() {
		if (hp == 1) {
			try {
				// 让占有this的减血线程,暂时释放对this的占有,并等待
				this.wait();    
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		hp = hp - 1;
		System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
	}

	public void attackHero(LOLHero h) {
		h.hp -= damage;
		System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n", name, h.name, h.name, h.hp);
		if (h.isDead())
			System.out.println(h.name + "死了!");
	}

	public boolean isDead() {
		return 0 >= hp ? true : false;
	}

}

[3], about wait, notify and notifyAll

Pay attention to what objects the wait() and notify() methods are on?
 

public synchronized void hurt() {

    。。。
    this.wait();
    。。。

}
public synchronized void recover() {

    。。。
    this.notify();

}

What needs to be emphasized here is that the wait method and notify method are not methods on the Thread thread, they are methods on the Object

Because all Objects can be used as synchronization objects, to be precise, wait and notify are methods on synchronization objects.

method meaning
wait() Let the thread occupying this synchronization object temporarily release the current occupation and wait. So there is a precondition to call wait, it must be in the synchronized block, otherwise an error will occur.
notify() Notify a thread waiting on this synchronization object that you can wake up and have a chance to reoccupy the current object.
notifyAll() Notify all threads waiting on this synchronization object that you can wake up and have a chance to reoccupy the current object.

(7), thread pool

The start and end of each thread are time-consuming and resource-intensive. 
If a lot of threads are used in the system, a large number of start and end actions will cause the system's performance to become stuck and slower response. 
In order to solve this problem, the design idea of ​​thread pool is introduced. 
The thread pool model is very similar to the producer consumer model, the objects consumed are tasks that can be run one by one

[1], thread pool design ideas

The thread pool idea is very close to the producer consumer model .
① Prepare a task container
② Start 10 consumer threads at once
③ The task container is empty at the beginning, so the threads are all waiting on it.
④ Until an external thread throws a "task" into the task container, a consumer thread will be awakened to notify.
⑤ This consumer thread takes out the "task" and executes the task. After the execution is completed, it continues to wait for the next time The mission is coming.
⑥ If more tasks are added in a short period of time, then multiple threads will be awakened to perform these tasks.

In the entire process, there is no need to create new threads, but to recycle these existing threads

线ç¨æ ± 设计æè · ¯

[2], develop a custom thread pool

import java.util.LinkedList;

public class ThreadPoolTest {

	public static void main(String[] args) {
		
		// 线程池
		ThreadPool pool = new ThreadPool();

		for (int i = 0; i < 7; i++) {	// 循环发布任务
			
			Runnable task = new Runnable() {
				@Override
				public void run() {
					// System.out.println("执行任务");
					// 任务可能是打印一句话
					// 可能是访问文件
					// 可能是做排序
				}
			};

			pool.add(task);	// 向线程池中发布任务

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

}

class ThreadPool {

	// 线程池大小
	int threadPoolSize;

	// 任务容器
	LinkedList<Runnable> tasks = new LinkedList<Runnable>();

	// 试图消费任务的线程

	public ThreadPool() {
		threadPoolSize = 10;

		// 启动10个任务消费者线程
		synchronized (tasks) {
			for (int i = 0; i < threadPoolSize; i++) {
				new TaskConsumeThread("任务消费者线程 " + i).start();
			}
		}
	}

	// 向线程池中发布任务
	public void add(Runnable r) {
		synchronized (tasks) {
			tasks.add(r);
			// 唤醒等待的任务消费者线程
			tasks.notifyAll();
		}
	}

	class TaskConsumeThread extends Thread {
		public TaskConsumeThread(String name) {
			super(name);
		}

		Runnable task;

		public void run() {
			System.out.println("启动: " + this.getName());
			while (true) {
				synchronized (tasks) {
					while (tasks.isEmpty()) {
						try {
							tasks.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					task = tasks.removeLast();
					// 允许添加任务的线程可以继续添加任务
					tasks.notifyAll();

				}
				System.out.println(this.getName() + " 获取到任务,并执行");
				task.run();
			}
		}
	}

}

Results of the:

[3], use java's own thread pool

The thread pool class ThreadPoolExecutor is under the package java.util.concurrent

ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

The first parameter 10 means that this thread pool initializes 10 threads to work in it. The
second parameter 15 means that if 10 threads are not enough, it will automatically increase to a maximum of 15 threads. The
third parameter 60 is combined with the fourth parameter. TimeUnit.SECONDS means that after 60 seconds, the extra threads will be recycled if they have not received any work. Finally, there are only 10 in the pool. The
fourth parameter TimeUnit.SECONDS is the same as the
fifth parameter new LinkedBlockingQueue() above. The set of tasks

execute method is used to add new tasks

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorTest {

	static int sum = 0;

	public static void main(String[] args) throws InterruptedException {

		ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

		for (int i = 0; i < 30; i++) {

			int n = threadPool.getCorePoolSize();
			
			threadPool.execute(new Runnable() {

				@Override
				public void run() {
					System.out.println("任务" + this.hashCode()%1000 + "正在运行......此时线程池内线程数为:" + n);
				}

			});

		}

	}

}

Results of the:

(8), Lock object

Similar to synchronized, lock can also achieve the effect of synchronization

[1], use Lock object to achieve synchronization effect

Lock is an interface, in order to use a Lock object, you need to use

Lock lock = new ReentrantLock();


Similar to synchronized (someObject), the lock() method means that the current thread occupies the lock object. Once occupied, other threads cannot occupy it.
The difference with synchronized is that once the synchronized block ends, it will automatically release the occupation of someObject. The lock must be manually released by calling the unlock method. In order to ensure the execution of the release, unlock() is often placed in finally.

[2], trylock method

Synchronized is not occupied without rest, will always try to occupy it.
Unlike the synchronized method, the Lock interface also provides a trylock method.
Trylock will try to occupy within the specified time range, if the account is successful, it will snap. If the time is up and the occupation is unsuccessful, turn your head and leave~

Note: Because trylock may succeed or fail, so when unlocking the lock later, you need to judge whether the occupation is successful. If the occupation is not successful, unlock it. Throw an exception

[3], thread interaction

Use the synchronized method for thread interaction. The wait, notify and notifyAll methods of the synchronization object are used.

Lock also provides a similar solution. First, obtain a Condition object through the lock object, and then call the Condition object: await, signal, signalAll method

Note: Not the wait, nofity, notifyAll methods of the Condition object, but await, signal, signalAll

[4]、Lock和synchronized的区别

1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。

2. Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。

3. synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁。

(9), atomic access

Atomic operations are uninterruptible operations

After JDK6, a new package java.util.concurrent.atomic has been added, which contains various atomic classes, such as AtomicInteger.
And AtomicInteger provides various self-increment and self-decrement methods, which are all atomic. In other words, the incrementAndGet method is thread-safe. Only one thread can call this method at a time.

Guess you like

Origin blog.csdn.net/qq_37164975/article/details/82756233