Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the ways to implement threads?

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what are the ways to implement threads". The content of the explanation in the article is simple and clear, and it is easy to learn and understand. let's follow the editor's train of thought. Let's study and learn "what are the ways to implement threads?"

Method 1: inherit the Thread class

Package com.thread;// implements the custom thread class public class MyThread extends Thread {/ / thread body @ Override public void run () {System.out.println ("Hello, I am the defined thread created by extends Thread");} public static void main (String [] args) {/ / instantiate the custom thread class instance Thread thread = new MyThread () by inheriting the Thread class / call the start () instance method to start the thread thread.start ();} 123456789101112131415

Advantages: simple to implement, only need to instantiate an instance of the inherited class, you can use thread disadvantages: lack of expansibility, Java is a single inheritance language, if a class has inherited other classes, you cannot implement custom threads in this way

Method 2: implement the Runnable interface

Package com.thread;public class MyRunnable implements Runnable {/ / thread body @ Override public void run () {System.out.println ("Hello, I am the defined thread created by implements Runnable");} public static void main (String [] args) {/ / Thread execution target object MyRunnable myRunnable = new MyRunnable (); / / actual thread object Thread thread = new Thread (myRunnable) / / start thread thread.start ();}}

Advantages:

Good expansibility, on the basis of which you can inherit other classes and achieve other necessary functions

For scenarios where multi-threads share resources, it has natural support and is suitable for scenarios where multiple threads deal with a resource.

Disadvantages: the process of constructing thread instances is relatively cumbersome

Method 3: implement the Callable interface

Package com.thread;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class MyCallable implements Callable {@ Override public String call () throws Exception {return "Hello, I am the defined thread created by implements Callable";} public static void main (String [] args) {/ / Thread execution target MyCallable myCallable = new MyCallable () / / wraps the thread execution target, because the constructor of Thread can only accept the implementation class of the Runnable interface, while the FutureTask class implements the Runnable interface FutureTask futureTask = new FutureTask (myCallable); / / incoming thread execution target, instantiating thread object Thread thread = new Thread (futureTask); / / starting thread thread.start (); String result = null Try {/ / get thread execution result result = futureTask.get ();} catch (InterruptedException e) {e.printStackTrace ();} catch (ExecutionException e) {e.printStackTrace ();} System.out.println (result);}}

Advantages:

Good expansibility

Support for multithreading of the same resource

Has a return value and can throw checked exceptions

Disadvantages:

Compared with the way to implement the Runnable interface, it is more complicated.

Summary

After analyzing these three methods, we can find that mode one and mode two essentially implement the thread body (the implementation in the thread (run) method) by implementing the Runnable interface and rewriting the run () method, passing the instance of the interface implementation class to the thread thread class. Here, the instance of the Runnable interface implementation class is taken as the thread execution target for thread instance execution. For mode 3, in fact, this is also the case, because the Thread class can only execute the execution goal of the Runnable interface implementation class, so the implementation class of the Callable interface needs to be wrapped into the implementation class of the Runnable interface (wrapped by the FutureTask class that implements the Runnable interface), so that the Thread class can receive instances of the Callable interface implementation class. It can be seen that the adapter pattern is used here!

To sum up, there is a usage paradigm for all three implementations, that is, first implement the thread to execute the target object (including the tasks the thread is going to perform), and then take the target object as construction parameters to instantiate the Thread instance to get the thread! In essence is to achieve a thread body, by the Thread to execute the thread body, to achieve the effect of opening the thread to execute the task! However, each of the three implementation methods has its own advantages and disadvantages, and when using it, it should be combined with specific requirements to choose the appropriate implementation for development!

Life cycle of a thread

After the above code demonstration, we know how to implement threads, but if we want to make better use of threads, we also need to understand the state of running threads and the transition between states (that is, the life cycle of threads). Only in this way can we analyze the cause of the problem when there is a problem in the running of multi-threaded program, so as to locate and solve the problem quickly and accurately!

First, take a look at the description of thread state given in the Thread class:

/ * six states in the thread life cycle * NEW: the state of the thread instance that has not called start () * RUNNABLE: the state of the thread executing in the virtual machine * BLOCKED: the state of the thread waiting on the monitor lock * WAITING: the state of the thread waiting for other threads to perform a specific operation * TIMED _ WAITING: state of the thread waiting for other threads to perform the timeout operation * TERMINATED: state of the exiting thread * given point in time A thread will only be in one of the following states, which are only the thread state at the virtual machine level and do not reflect the state of the thread in any operating system * / public enum State {/ / the state of the thread instance opened by calling start () NEW, / / the state of the thread executing in the virtual machine or waiting to be executed But this state also includes the RUNNABLE where the thread is waiting for the processor resource, / / the state of the thread waiting on the monitor lock, such as entering the synchronized synchronization code block or the synchronization method failed BLOCKED, / / the state of the thread waiting for other threads to perform a specific operation For example, the thread executes the following methods: Object.wait with no timeout, Thread.join with no timeout, LockSupport.park WAITING, / / the state of the thread waiting for other threads to perform the timeout operation For example, the thread executes the following methods: Thread.sleep, Object.wait with timeout / / Thread.join with timeout, LockSupport.parkNanos, LockSupport.parkUntil TIMED_WAITING, / / the state of the exiting thread TERMINATED;}

New (New): after the thread instance is new, the thread instance is in the new state before calling the start () method.

Runnable (Runnable): when a thread instance calls the start () method, before the thread scheduler allocates processor resources, the thread instance is in a runnable state, or after the thread scheduler allocates processor resources to a thread, the thread instance is in a runnable state.

Waitting: when a thread is running, the thread is waiting when it executes the obj.wait () or Thread.join () method, Thread.join, LockSupport.park, and Thread.sleep ()

Timed Waitting: when a thread is running, the thread is in a timeout wait state when it executes the obj.wait (long), Thread.join (long), LockSupport.parkNanos, LockSupport.parkUntil, and Thread.sleep (long) methods

Blocked: when a thread is running, the acquisition of the lock fails, the thread instance enters the waiting queue, and the state becomes blocked

Terminated: when a thread finishes execution or ends abnormally early, the thread enters a termination state

State transition of thread

As mentioned above, the state of a thread at a certain point in time can only be one of the above six states; however, the state of a thread will change from one state to another while the program is running. then the thread state transition diagram is given below to help us clearly understand the state transition process of the thread:

Above we have a clear understanding of the implementation and state of the thread, so through the above content, we can also find that there are actually many methods, which we have not introduced in detail, such as start (), yield (), wait (), notify (), notifyAll (), sleep (), join () and so on. Most of these methods come from the key thread class Thread in JDK. Let's take a look at the source code of the Thread class. What are the methods often encountered in multithreaded programming, and the uses of these methods

Thread class Thread source code

Instance synchronization method: join ()

/ * waiting for the thread calling this method to finish execution * @ throws InterruptedException if any thread interrupts the current thread, this exception will be thrown and the interrupt flag bit will be cleared * / public final void join () throws InterruptedException {join (0) } / * wait for millis for milliseconds at most. When the time is up, no matter whether the execution is completed or not, it will be returned * if millis is 0 Then it means that it will not be returned until the thread finishes execution * the implementation of this method is based on the loop checking whether the current thread is alive to determine whether to call the wait method of the current instance to achieve * @ param millis wait time * @ throws IllegalArgumentException illegal parameter exception * @ throws InterruptedException if any thread interrupts the current thread, this exception will be thrown At the same time, the interrupt flag bit is cleared * / public final synchronized void join (long millis) throws InterruptedException {long base = System.currentTimeMillis () Long now = 0; if (millis

< 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >

= 500000 | | (nanos! = 0 & & millis = = 0) {millis++;} join (millis);}

Interrupt method, interrupt detection method and activity judgment method:

/ * interrupt the current thread * if the current thread blocks wait (), wait (long), wait (long, int) of Object Or * join (), join (long), join (long, int) and sleep (long), sleep (long, int), etc. * then the interrupt flag bit will be cleared and an interrupt exception will be received * non-static method * / public void interrupt () {if (this! = Thread.currentThread ()) checkAccess () Synchronized (blockerLock) {Interruptible b = blocker; if (b! = null) {interrupt0 (); / / Just to set the interrupt flag b.interrupt (this); return;}} interrupt0 () } / * detects whether the current thread has been interrupted, this method clears the interrupt flag of the current thread * that is, if the method is called twice in a row and the thread is interrupted before the first call, the first call returns true, and the second call returns false * @ return true if the current thread has been interrupted, return true Otherwise, return false * static method * / public static boolean interrupted () {return currentThread () .isInterrupted (true) } / * detects whether the current thread has been interrupted. This method does not clear the interrupt flag of the current thread * non-static method * / public boolean isInterrupted () {return isInterrupted (false) } / * according to the parameter value, decide whether to clear the flag bit after judging the interrupt flag bit * instance method * / private native boolean isInterrupted (boolean ClearInterrupted); / * detect whether a thread is still alive, which means that it has been started but not terminated * instance method * / public final native boolean isAlive ()

Thread scheduling

Implementation of Java threads: the Java thread model is based on the operating system native thread model; the thread model only affects the concurrent scale and operation cost of threads, and there is no difference in the writing and running process of Java programs.

Thread priority

The time division form is the basic thread scheduling form adopted by the modern operating system. The operating system divides the CPU resources into time slices and allocates them to threads. The threads use the acquired time slices to execute tasks. After the time slices are used, the operating system schedules the threads, and other threads that get the time slices begin to execute. Then, the number of time slices that a thread can allocate determines how many processor resources the thread uses, and the thread priority is the thread attribute that determines whether the thread can get more or less processor resources.

You can set the priority of a thread to make the thread get the processor execution time differently, but it is not reliable to realize that the thread gets the processor execution time in this way (because the priority of the system and the priority in Java do not correspond one to one, it is possible that multiple thread priorities in Java correspond to the same priority in the system). There are 10 thread priorities in Java, from 1 (Thread.MIN_PRIORITY) to 10 (Thread.MAX_PRIORITY), and the default priority is 5; therefore, the correctness of the program cannot be judged by the thread priority.

# # Thread scheduling

Thread scheduling refers to the process that the system allocates the right to use the processor to the thread. The main scheduling methods are: preemptive thread scheduling and collaborative thread scheduling.

Preemptive thread scheduling

Each thread is allocated execution time by the system, and the thread switching is not determined by the thread itself; the default thread scheduling method used by Java is preemptive thread scheduling; we can make the currently executing thread give up the execution time through Thread.yield (), but there is no way for the thread to get the execution time.

# Collaborative thread scheduling

The execution time of each thread is controlled by the thread itself. After executing the task, the thread actively notifies the system and switches to another thread.

Advantages and disadvantages of two thread scheduling methods

Advantages of collaborative type: easy to implement, thread safety problems can be avoided through thread switching control; disadvantages of collaborative type: once there is a problem with the current thread, it may affect the execution of other threads and eventually lead to system crash The advantage of preemption: a problem with one thread does not affect the execution of other threads (the execution time of the thread is allocated by the system, therefore, the system can allocate processor execution time to other threads to avoid the failure of one thread causing the entire system to crash)

Conclusion

In Java, the thread scheduling strategy is mainly preemptive scheduling strategy. It is precisely because of the preemptive scheduling strategy that the actual running process of multithreaded programs is quite different from the order we logically understand, that is, the execution of multithreaded programs is uncertain, which will lead to some thread safety problems. So, what is thread safety?

Thread safety

Definition of thread safety

To put it simply, thread safety means that operations executed concurrently by multiple threads do not require any external control or coordination to ensure that the execution results of the program are consistent with the developers' expected results. then the multithreaded program is thread safe; note: the thread safety problem must be based on the premise that multiple threads access shared data. If multiple threads do not access the same variable, then there is no thread safety problem.

Classification of thread safety

The concept of thread safety is not only divided into thread safety and non-thread safety. According to the degree of thread safety, the operations of various shared variables can be divided into five cases: immutable, absolute thread safety, relative thread safety, thread compatibility and thread opposition.

Immutable: if the shared variable is an immutable object, then the multithreaded operation on the shared variable must be thread-safe, because the object is immutable, so no thread can change the state of the shared variable. There will be no dirty reading and other phenomena.

If a shared variable is a variable of a basic data type, you can use the final keyword to ensure that it is immutable

If the shared variable is an object, then you need to ensure that the behavior of the object will not change the state of the object. You can modify all fields of a class with the final keyword, so you can ensure that the object of the class is immutable, such as the java.lang.String class.

Absolute thread safety: without any synchronous processing on the calling side, the code can be guaranteed to be thread safe in the scenario of multithread concurrency, that is, the result of multithread concurrent execution is in line with the expected results. Most of the classes marked as thread safe in Java API are not absolutely thread safe.

Relative thread safety: most of the classes labeled thread-safe in Java API are relatively thread-safe, that is, thread-safe in the general sense, ensuring that they are thread-safe when operating on shared variables alone, and can be called without additional safeguards; for example, Vector, HashTable, or collections wrapped by the synchronizedCollection () method of Collections, etc.

Thread compatibility: thread compatibility means that the object itself is not thread-safe, but * * objects can be safely used in a concurrent environment by using synchronization means correctly on the calling side, which is generally non-thread-safe; most classes in Java API are thread-compatible, such as ArrayList, HashMap, etc.

Thread antagonism: no matter what synchronization measures are taken by the caller, thread safety in a multithreaded environment can not be guaranteed; thread antagonism is rare.

Solution to the problem of thread safety

After introducing the principle of thread scheduling, it can be analyzed that the cause of the thread safety problem is the uncertainty of the execution order of multiple threads, so there is no unexpected situation when multiple threads operate a resource at the same time. The compiler and processor will reorder the executed instructions, which lead to thread safety problems.

So, in actual development, what we generally need to solve are the above two kinds of thread safety problems: relative thread safety and thread compatibility; then, for these two kinds of problems, it can be subdivided into three types of problems: visibility, atomicity and orderliness. Here, without subdivision, we give common solutions to thread safety problems.

Thread safety problems recur

Let's take a look at the thread safety problems that may arise when using multithreaded programming in combination with specific code:

Package com.thread; public class ThreadSafe implements Runnable {/ / static variable, all objects share private static int count = 0; @ Override public void run () {for (int I = 0; I)

< 100 ; i++){ count(); } } public void count(){ try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count++; } public static void main(String[] args) throws InterruptedException { ThreadSafe threadSafe1 = new ThreadSafe(); ThreadSafe threadSafe2 = new ThreadSafe(); Thread thread1 = new Thread(threadSafe1); Thread thread2 = new Thread(threadSafe2); thread1.start(); thread2.start(); Thread.currentThread().sleep(1000); System.out.println(count); } } 运行结果: 这一段代码的目的是开启两个线程对同一个变量分别进行100次的累加,按照正常的逻辑(串行化执行),累加后的结果应该为200,但是实际输出的结果却是190,显然这和我们的预期结果不同,这就是线程安全问题;我们分析一下,为什么会出现这样的情况,之前提到过,多线程执行的时候代码执行的顺序具有不确定性,那么就可能出现,线程1(thread1)在获取到count的值之后,CPU执行权被分配给了线程2(thread2),线程2获取到的值与线程1获取到的相同,那么两个线程累加操作执行后,相当于只累加来一次,这样就会导致线程不安全问题产生;那么,如何解决这个问题,我们可以利用Java中的synchronized关键字对线程体进行同步,代码如下: package com.thread; public class ThreadSafeTwo implements Runnable{ //静态变量,所有对象共享 private static int count = 0; @Override public void run() { //这里对线程体进行同步 synchronized(ThreadSafeTwo.class){ for(int i = 0 ; i < 100 ; i++){ count(); } } } public void count(){ try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count++; } public static void main(String[] args) throws InterruptedException { ThreadSafeTwo threadSafe = new ThreadSafeTwo(); Thread thread1 = new Thread(threadSafe); Thread thread2 = new Thread(threadSafe); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(count); } } 同步处理后代码执行的结果如下: 显然,经过同步后的代码,就可以保证多线程并发执行的情况下,结果依然符合预期结果;关于synchronized关键字的实现原理将会另起一文进行分析,下面我们看一下,synchronized关键字的使用方式有哪些? ** synchronized关键字的使用方式** synchronized同步代码块 锁的对象为指定的对象 synchronized同步实例方法 锁的对象为当前实例 synchronized同步静态方法 锁的对象为Class对象 synchronized关键字的应用实例 线程安全的单例模式实现 package com.thread; public class SingleTonThreadSafe { //属性私有化,volatile实现内存可见性、禁止指令重排序 private volatile static SingleTonThreadSafe singleTonThreadSafe = null; //无参构造函数私有化 private SingleTonThreadSafe(){} //静态方法外部使用,获取对象实例 public static SingleTonThreadSafe getInstance(){ //第一次判断,避免不必要的加锁 if(singleTonThreadSafe == null){ //同步实例化代码块 synchronized(SingleTonThreadSafe.class){ //再次检测,避免其它线程已经实例化 if(singleTonThreadSafe == null){ //实例化,其他线程立即可见 singleTonThreadSafe = new SingleTonThreadSafe(); } } } return singleTonThreadSafe; } } synchronized同步锁的使用注意点 死锁 定义:多个线程互相等待已被对方占有的锁,同时都不释放自己已经占有的锁,导致线程之间陷入僵持,致使系统不可用 形成条件:互斥锁、锁只能主动释放、循环等待 避免策略:顺序加锁、超时获取自动放弃、死锁检测 活锁 定义:线程等待被其他线程唤醒,但是实际没有线程来唤醒,导致线程一直无法恢复到运行状态 避免策略:编程时有等待,就必须有对应的唤醒 线程间通信 如果你的多线程程序仅仅是每个线程独立完成各自的任务,相互之间并没有交互和协作,那么,你的程序是无法发挥出多线程的优势的,只有有交互的多线程程序才是有意义的程序,否则,还不如使用单线程执行多个方法实现程序来的简单、易懂、有效! 那么,线程间进行交互通信的手段有哪些呢?下面,将给出常用的多线程通信的实现手段以及相应的代码示例,并结合具体的代码进行分析,对其中需要注意的地方进行突出提示; 等待通知机制 我们先看这样一个场景:线程A修改了对象O的值,线程B感知到对象O的变化,执行相应的操作,这样就是一个线程间交互的场景;可以看出,这种方式,相当于线程A是发送了消息,线程B接收到消息,进行后续操作,是不是很像生产者与消费者的关系?我们都知道,生产者与消费者模式可以实现解耦,使得程序结构上具备伸缩性;那么Java中如何实现这种功能呢? 一种简单的方式是,线程B每隔一段时间就轮询对象O是否发生变化,如果发生变化,就结束轮询,执行后续操作; 但是,这种方式不能保证对象O的变更及时被线程B感知,同时,不断地轮询也会造成较大的开销;分析这些问题的症结在哪?其实,可以发现状态的感知是拉取的,而不是推送的,因此才会导致这样的问题产生; 那么,我们就会思考,如何将拉取变为推送来实现这样的功能呢? 这就引出了Java内置的经典的等待/通知机制,通过查看Object类的源码发现,该类中有三个方法,我们一般不会使用,但是在多线程编程中,这三个方法却是能够大放异彩的!那就是wait()/notify()/notifyAll(); /** * 调用此方法会导致当前线程进入等待状态直到其它线程调用同一对象的notify()或者notifyAll()方法 * 当前线程必须拥有对象O的监视器,调用了对象O的此方法会导致当前线程释放已占有的监视器,并且等待 * 其它线程对象O的notify()或者notifyAll()方法,当其它线程执行了这两个方法中的一个之后,并且 * 当前线程获取到处理器执行权,就可以尝试获取监视器,进而继续后续操作的执行 * 推荐使用方式: * synchronized (obj) { * while () * obj.wait(); * ... // Perform action appropriate to condition * } * @throws IllegalMonitorStateException 如果当前线程没有获取到对象O的监视器时,抛出异常 * @throws InterruptedException 如果在调用了此方法之后,其他线程调用notify()或者notifyAll() * 方法之前,线程被中断,则会清除中断标志并抛出异常 */ public final void wait() throws InterruptedException { wait(0); } /** * 唤醒等待在对象O的监视器上的一个线程,如果多个线程等待在对象O的监视器上,那么将会选择其中的一个进行唤醒 * 被唤醒的线程只有在当前线程释放锁之后才能够继续执行. * 被唤醒的线程将会与其他线程一同竞争对象O的监视器锁 * 这个方法必须在拥有对象O的监视器的线程中进行调用 * 同一个时刻,只能有一个线程拥有该对象的监视器 * @throws IllegalMonitorStateException 如果当前线程没有获取到对象O的监视器时,抛出异常 */ public final native void notify(); /** * 唤醒等待在对象O的监视器上的所有线程 * 被唤醒的线程只有在当前线程释放锁之后才能够继续执行. * 被唤醒的线程将会与其他线程一同竞争对象O的监视器锁 * 这个方法必须在拥有对象O的监视器的线程中进行调用 * 同一个时刻,只能有一个线程拥有该对象的监视器 * @throws IllegalMonitorStateException 如果当前线程没有获取到对象O的监视器时,抛出异常 */ public final native void notifyAll(); 下面看一下如何通过这三个方法实现经典的等待通知机制吧! 按照JDK中推荐的使用方式实现了等待通知样例代码如下: package com.thread; public class WaitAndNotify { //轮询标志位 private static boolean stop = false; //监视器对应的对象 private static Object monitor = new Object(); //等待线程 static class WaitThread implements Runnable{ @Override public void run() { synchronized(monitor){ //循环检测标志位是否变更 while(!stop){ try { //标志位未变更,进行等待 monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //被唤醒后获取到对象的监视器之后执行的代码 System.out.println("Thread "+Thread.currentThread().getName()+" is awakened at first time"); stop = false; } //休眠1秒之后,线程角色转换为唤醒线程 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //与上述代码相反的逻辑 synchronized(monitor){ while(stop){ try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } monitor.notify(); stop = true; System.out.println("Thread "+ Thread.currentThread().getName()+" notifies the waitted thread at first time"); } } } //通知线程 static class NotifyThread implements Runnable{ @Override public void run() { synchronized (monitor){ while(stop){ try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } stop = true; monitor.notify(); System.out.println("Thread "+ Thread.currentThread().getName()+" notifies the waitted thread at first time"); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (monitor){ while(!stop){ try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Thread "+Thread.currentThread().getName()+" is awakened at first time"); } } } public static void main(String[] args){ Thread waitThread = new Thread(new WaitThread()); waitThread.setName("waitThread"); Thread notifyThread = new Thread(new NotifyThread()); notifyThread.setName("notifyThread"); waitThread.start(); notifyThread.start(); } } 通过上述代码,可以提炼出等待通知机制的经典模式: 等待方实现步骤: 加锁同步 条件不满足,进入等待,被唤醒之后,继续检查条件是否满足(循环检测) 条件满足,退出循环,继续执行后续代码 对应的伪代码: synchronized(obj){ while(condition不满足){ obj.wait(); } //后续操作 }123456 通知方实现步骤: 加锁同步 条件不满足,跳过循环检测 设置条件并唤醒线程 对应的伪代码: synchronized(obj){ while(condition不满足){ obj.wait(); } 更新condition obj.notify(); //后续操作 } 生产者消费者模式 基于等待通知机制,我们可以很容易地写出生产者消费者模式的代码,下面给出一个实现样例代码: package com.thread; public class ProducerAndConsumer { //商品库存 private static int storeMount = 0; //监视器对应的对象 private static Object monitor = new Object(); //生产者线程 static class ProducerThread implements Runnable{ @Override public void run() { try { produce(); } catch (InterruptedException e) { e.printStackTrace(); } } public void produce() throws InterruptedException { while(true){ synchronized(monitor){ //循环检测库存是否大于0,大于0表示还有商品可以消费,线程等待消费者消费商品 while(storeMount >

0) {monitor.wait ();} / / the code System.out.println ("Thread" + Thread.currentThread () .getName () + "begin produce goods") executed after being awakened to get the object's monitor / / produce goods storeMount = 1; / / awaken consumers monitor.notify (); Thread.sleep (1000) } / / Consumer thread static class ConsumerThread implements Runnable {@ Override public void run () {try {consume () } catch (InterruptedException e) {e.printStackTrace () }} public void consume () throws InterruptedException {while (true) {synchronized (monitor) {/ / check whether the inventory is not zero, if not, then there are goods available for consumption Otherwise, wait for the producer to produce goods while (storeMount = = 0) {monitor.wait () } / / Consumer goods storeMount = 0; / / Wake up the producer thread monitor.notify () System.out.println ("Thread" + Thread.currentThread (). GetName () + "begin consume goods"); Thread.sleep (1000) } public static void main (String [] args) {Thread producerThread = new Thread (new ProducerThread ()); producerThread.setName ("producerThread"); Thread consumerThread = new Thread (new ConsumerThread ()); consumerThread.setName ("consumerThread") ProducerThread.start (); consumerThread.start ();}}

The execution result is shown in the following figure:

The above code example demonstrates a scenario in which a producer produces goods and a consumer consumes goods. For a producer with multiple consumers, multiple producers and multiple consumers, you only need to change the wake-up method to notifyAll (), otherwise, there will be hunger!

Summary

First of all, this paper gives the form of thread scheduling in Java, leads to the thread safety problems that need to be solved in multithreaded programming, analyzes the thread safety problems, and gives the common means to solve the thread safety problems (lock synchronization). Finally, combined with the built-in waiting notification mechanism of Java, the sample code is displayed and analyzed, and the classical programming paradigm of waiting notification mechanism is given. Finally, based on the waiting notification mechanism, the implementation example of producer-consumer model is given. I hope this article can give some help to friends who want to learn multithreaded programming. If there is anything incorrect, I hope to point out, thank you very much!

Attention to detail

Thread classification

Thread.setDaemon (true) to set the thread property to the daemon thread, which must be performed before the thread calls the start () method

The finally code block in the daemon thread does not necessarily execute, so do not rely on the finally code block in the daemon thread to complete the release of resources.

User threads: most threads are user threads to complete business functions

Daemon thread: supporting thread, mainly used for background scheduling and supporting work, such as GC thread. When there is no non-daemon thread in JVM, JVM will exit.

The way threads interact

Join

Sleep/interrupt

Wait/notify

How to start a thread

Threads can only be started by calling the start () method through the thread object

The meaning of the start () method is that the current thread (parent thread) synchronously tells the virtual machine that as long as the thread planning period is idle, it should immediately start the thread that called the start () method.

Before starting a thread, you should set the thread name so that it can serve as a reminder when using Jstack to analyze the thread's health in the program.

The way a thread is terminated

The release of thread resources is not necessarily guaranteed after the call

After the call, the thread will not release the resources already occupied, which can easily lead to deadlock problems.

The thread carries on the interrupt flag to the target thread by calling the interrupt () method of the target thread, and the target thread responds to the interrupt by detecting its own interrupt flag bit (interrupted () or isInterrupted ()), releases resources and finally terminates the thread operation.

The method that throws an InterruptedException exception clears the interrupt flag bit of the thread before throwing the exception, and then throws the exception

Interrupt detection mechanism

Suspend () / resume () (deprecated)

Stop () (deprecated)

Lock release:

End of execution of a synchronization method or synchronization code block (normal, abnormal end)

Synchronous method or synchronous code block lock object calls the wait method

Situations in which the lock will not be released:

Call the static methods yield () and sleep () of the Thead class

Call the thread object's suspend ()

Thank you for your reading. The above is the content of "what are the ways to implement threads?" after the study of this article, I believe you have a deeper understanding of the implementation of threads. the specific use also needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report