Table of contents
Getting to know multithreading
What is multithreading?
Thread
Thread is the smallest unit that the operating system can perform operation scheduling.Threads are contained in processes, is the actual operating unit in the process .
Simple understanding: functions that are independent of each other and can run simultaneously in the application software
So what is a process?
Process
A process is the basic execution entity of a program. For example, if you open the task manager, you can see that there are many processes in it.
多线程作用:提高效率
Two concepts of multithreading
Concurrency
Concurrency: At the same time, multiple instructions are executed alternately on a single CPU
Parallel
Parallel: At the same time, multiple instructions are executed simultaneously on multiple CPUs
Implementation of multithreading
① Implementation by inheriting the Thread class
The first way to start multithreading:
1. Define a class by yourself to inherit Thread
2.Rewrite the run method
3. Create an object of the subclass and start the thread
public class MyThread extends Thread{
@Override
public void run() {
//书写线程要执行代码
for (int i = e; i < 100; i++) {
system.out.println(getName( ) + "Helloworld" );
}
}
}
public static void main( String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread() ;
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
② Realize by implementing the Runnable interface
The second way to start multi-threading:
1. Define a class by yourself to implement the Runnable interface 2. Rewrite the run method inside
3. Create an object of your own class
4. Create an object of the Thread class and start the thread
public static void main(String[] args) {
//创建实现Runnable接口的类的对象
RunnableInf run = new RunnableInf();
//创建线程对象
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//给线程设置名字
t1.setName("线程一");
t2.setName("线程二");
//开启线程
t1.start();
t2.start();
}
public class RunnableInf implements Runnable{
@Override
public void run() {
//书写线程执行代码
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"hello world");
}
}
}
③ Realize by using Callable interface and Future interface
The third implementation of multithreading:
Features: Can get multi-threaded runningresult
The steps are as follows
1. Create a class MyCallable to implement the callable interface
2. Rewrite call (it has a return value, indicating the result of multi-threaded operation)
3. Create an object of MyCallable (representing the tasks to be performed by multithreading)
4. Create the object of FutureTask (function to manage the result of multi-threaded operation)
5.Create an object of the Thread class and start it (representing a thread)
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Mycallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> ft = new FutureTask<>(mc ) ;
//创建线程的对象
Thread t1 = new Thread(ft);
//启动线程
t1.start();
//获取线程运行结果
Integer result = ft.get();
System.out.println(result);
}
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//求100之间的和
int sum = 0;
for (int i = 0; i <= 100; i++ ){
sum += i;
}
return sum;
}
}
Comparison of three implementations
common member methods
method details
void setName(string name)
sets the name of the thread (the constructor can also set the name)
Details :
1. If we do not set a name for the thread, the thread also has a default name
format: Thread-X (X serial number, starting from 0 )
2. If we want to set the name for the thread, we can use the set method to set it, or we can set it by the construction method (remember to inherit the construction method of the Thread class, the shortcut key Alt+Ins)
static Thread currentThread( )
Obtain the object
details of the current thread :
when the JVM virtual machine starts, it will automatically start multiple threads, one of which is called the main thread. Its
function is to call the main method and execute the code inside. In the past, all the code we wrote was actually running in the main thread
static void sleep(long time)
let the thread sleep for the specified time, the unit is milliseconds
Details :
1. Which thread executes this method, then which thread will stay here for the corresponding time
2. The parameter of the method: it means sleep The time, in milliseconds; 1 second = 1000 milliseconds
3. When the time is up, the thread will automatically wake up and continue to execute other codes below
Two ways of thread scheduling
- The biggest feature of preemptive scheduling is randomness, according topriorityTo seize cpu resources, the higher the priority, the greater the probability of preemption
- Non-preemptive scheduling features: once for you, once for me, regular
The priority is from 0 to 10, and the default priority is 5, as
you can see from the Thread source code
final void setDaemon(boolean on)
is set to the details of the daemon thread:
when other non-daemon threads are executed, the daemon threads will end one after another, not immediately
Easy to understand: According to the case in the picture below,
when the goddess thread is over, there is no need for a spare tire
goddess thread
spare tire thread
in the main method
The final result is that when thread 1 loops 10 times, the daemon threads end one after another (the loop will not end, nor will it stop immediately)
Daemon thread usage scenarios
such as QQ chat, file transfer
Letting Threads Explain
Insert thread demo
public static void main(String[] args) throws InterruptedException {
//创建实现Runnable接口的类的对象
RunnableInf run = new RunnableInf();
//创建线程对象
Thread t1 = new Thread(run);
//Thread t2 = new Thread(run);
//给线程设置名字
t1.setName("线程一");
//开启线程
t1.start();
//把t1线程插入到当前线程之前
t1.join(); //这个代码运行在哪个线程上,就插入在哪个线程前
for (int i = 0; i < 10; i++){
System.out.println("main线程"+i);
}
}
thread life cycle
Thread Safety Issues
Since threads seize CPU resources randomly , when performing business operations, multiple threads may execute the same business in parallel, resulting in data confusion or loss; for example, in the classic case of buying tickets, it is possible that one thread enters and executes buying tickets Logically, another thread also entered, resulting in the sale of two tickets with the same number. The classic problem is overselling, more than one ticket is sold, and there are tickets that are not sold.
The solution is naturally to add a lock to the executed business code to ensure atomicity
Method One: Synchronized Code Blocks
Lock down code that manipulates shared data
特点1:锁默认打卉,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开
The synchronous code block is used as follows, the case of selling 100 tickets
public class MyThread extends Thread{
//表示这个类的所有对象共享ticket数据
static int ticket = 0;
//创建锁对象,一定要唯一
static Object object = new Object();
@Override
public void run() {
while (true){
//同步代码块
synchronized (object){
if (ticket < 100){
ticket++;
System.out.println(getName()+"正在卖第" +ticket +"张票");
}else {
break;
}
}
}
}
}
public class Treaddemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}
Method 2: The synchronization method
is to add the synchronized keyword to the method
Feature 1: The synchronization method is to lock all the code in the method
Feature 2: The lock object cannot be specified by itself
Non-static: this
Static: The bytecode file object of the current class
The case of selling 100 tickets
public class MyRunnable implements Runnable{
int ticket = 0;//由于MyRunnalbe方法只new了一个对象,可以不用把ticket设置为共享变量
@Override
public void run() {
while (true){
if (method()) break;
}
}
private synchronized boolean method() {
synchronized (MyRunnable.class){
if ( ticket == 100 ){
return true;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName()+ "正在卖第"+ ticket +"张票");
}
}
return false;
}
}
public class Threaddemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
Method Three: Synchronous Method
In order to express more clearly how to lock and release the lock, JDK5 provides a new lock object Lock, which can be manually locked and released manually.
The Lock implementation provides a wider range of locking operations
than using synchronized methods and statements . Lock provides methods for acquiring and releasing locks.
void lock():获得锁
void unlock():释放锁
Lock is an interface that cannot be instantiated directly, and its implementation class ReentrantLock is usually used to instantiate the construction method of ReentrantLock
ReentrantLock(): Create an instance of ReentrantLock
Still taking the example of buying 100 tickets, the code for using the lock is as follows
public class LockThread extends Thread{
static int ticket = 0;
static Lock lock = new ReentrantLock();
public LockThread() {
}
public LockThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
//这次换用锁的方式
lock.lock();//上锁
try {
if (ticket < 100){
Thread.sleep(100);
ticket++;
System.out.println(getName()+"正在卖第" +ticket +"张票");
}else {
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
}
public class MainThread {
public static void main(String[] args) {
LockThread t1 = new LockThread("窗口一");
LockThread t2 = new LockThread("窗口二");
LockThread t3 = new LockThread("窗口三");
t1.start();
t2.start();
t3.start();
}
}
deadlock
In layman's terms, locks are nested. Thread 1 takes A lock. When thread 1 takes A lock, thread 2 enters B lock, and the operation in the middle of thread 1 needs to enter B lock again to execute the business and end; And the intermediate operation of thread 2 needs to re-enter the A lock to execute the business and end. Eventually both are waiting for the other to release the lock, resulting in a deadlock situation
producer and consumer
Waiting for wake-up mechanism
The waiting for wake-up mechanism will allow two threads to execute in turn, the standard one time for you and one time for me
Details: notify() wakes up a thread at random, which is not easy to control. Generally, the notifyAll() method is used
The following is a classic consumer and producer case code, the consumer is the eater, the producer is the chef, the middle control is the table, and the control thread executes
middle controller table
public class Desk {
/**
* 控制消费者和生产者的执行
*/
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数,也就是消费者需要的总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
diners
public class Eater extends Thread{
/**
* 1.循环
* 2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾执行的逻辑)
* 4.判断共享数据是否到了末尾(没到末尾执行的逻辑)
*/
@Override
public void run() {
while (true){
if (Desk.count == 0){
break;
}
synchronized (Desk.lock){
if (Desk.foodFlag == 0){
//先看是否有产品,没有就等待
try {
Desk.lock.wait();//让当前线程跟锁绑定
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
Desk.count--;
System.out.println("消费者正在消费产品,还需要消费数量:"+ Desk.count);
//消费完唤醒生产者继续做
Desk.lock.notifyAll();
//修改桌子状态
Desk.foodFlag = 0;
}
}
}
}
}
chef
public class Cooker extends Thread{
@Override
public void run() {
while (true){
if (Desk.count == 0){
break;
}
synchronized (Desk.lock){
if (Desk.foodFlag == 1){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
//修改桌上食物状态
Desk.foodFlag = 1;
System.out.println("生产者生产了面条");
//唤醒消费者
Desk.lock.notifyAll();
}
}
}
}
}
main method
public class TestDemo {
public static void main(String[] args) {
Eater e = new Eater();
Cooker c = new Cooker();
e.setName("消费者");
c.setName("生产者");
e.start();
c.start();
}
}
important point:
Desk.lock.wait(); //Let the current thread bind to the lock
Desk.Lock.notifyAll(); //Wake up all threads bound to this lock
The reason why the lock object is called here is to avoid the notifyAll() method from waking up all threads (including those outside the method such as system threads, etc.), call with an object, which can indicate which thread is awakened
(here, call with the lock object in the Desk class, because the lock object is unique, and it is only new once in the Desk class)
Thread Pool
Use the thread pool as a container to store threads
①Create a pool, which is empty
②When submitting a task, the pool will create a new thread object. After the task is executed, the thread will be returned to the pool. When submitting the task again next time, there is no need to create a new thread, and the existing thread can be reused directly.
③But if there is no idle thread in the pool when submitting the task, and no new thread can be created, the task will queue up
Executors: The thread pool tool class returns different types of thread pool objects by calling methods.
custom thread pool
The thread pool workflow, it will have the number of core threads, the number of temporary threads, the length of the queue, and idle time, which will affect the decision
When the number of tasks exceeds the number of core threads, redundant tasks will be queued in the queue.
When the number of tasks exceeds the number of core threads and the queue is full, temporary threads will be used to process tasks.
When the number of tasks exceeds the number of core threads, the maximum queue The sum of the number and the number of temporary threads. When fully loaded, redundant tasks will be discarded or processed according to the rejection policy of the thread pool
The idle time of the temporary thread will be destroyed when it exceeds the time specified by the thread pool initialization,
and the core thread will be destroyed only when the thread pool is destroyed
Creating a thread pool will find that there are 7 parameters
The final task rejection strategy has the following
Create a custom thread pool code as follows
Finally, thank you for reading, I hope this article can clear your doubts