Why concurrency is required
Concurrency is actually a decoupling strategy that helps us to separate what we do (the target) and when we do it (timing). Doing so can significantly improve the throughput of the application (getting more CPU scheduling time) and structure (multiple parts of the program are working together). As anyone who has ever done Java Web Development knows, the servlet program in the Java Web uses a single-instance multithreaded working mode supported by the servlet container, and the servlet container handles concurrency problems for you.
Misunderstandings and positive solutions
The most common misconceptions about concurrent programming include the following:
-Concurrency always improves performance (concurrency can significantly improve the performance of the program when the CPU has a lot of idle time, but when the number of threads is high, frequent scheduling switching between threads can degrade the performance of the system)
-Write concurrent programs without modifying the original design (the decoupling of the purpose and timing often has a huge impact on the system structure)
-Do not focus on concurrency issues when working with Web or EJB containers (only if you know what the container is doing to better use the container)
The following statements are an objective understanding of concurrency:
-Writing concurrent programs adds additional overhead to your code
-correct concurrency is very complex, even for very simple problems
-Defects in concurrency are not easily detected because they are not easy to reproduce
-concurrency often requires a fundamentally modified design strategy
Principles and techniques of concurrent programming a single responsibility principle
Separate concurrency-related code and other code (concurrency-related code has its own development, modification, and tuning life cycle).
Restricting data scopes
Two threads may interfere with each other when modifying the same field of a shared object, leading to unpredictable behavior, and one solution is to construct a critical section, but you must limit the number of critical sections.
Working with copies of data
A copy of the data is a good way to avoid sharing data, and the copied object is only treated in a read-only manner. Java The 5 java.util.concurrent package adds a class named Copyonwritearraylist, which is a subtype of the list interface, so you can think of it as a thread-safe version of ArrayList, which uses copy-on-write to create a copy of the data to avoid sharing Problems caused by concurrent access.
Threads should be as independent as possible
Let threads exist in their own world and do not share data with other threads. People who have experienced Java Web Development know that Servlets work as single-instance multithreading, and that the data associated with each request is passed through the parameters of the service method (or Doget or Dopost method) of the servlet subclass. As long as the code in the servlet uses only local variables, the servlet does not cause synchronization problems. Spring MVC's controllers do the same, and the objects obtained from the request are passed in as arguments to the method rather than as members of the class, and it is obvious that struts 2 is the opposite, so the action class as a controller in struts 2 is one instance of each request.
Concurrency programming prior to Java 5
The Java threading model is based on preemptive thread scheduling, which means:
- All threads can easily share objects in the same process.
- Any thread that can reference these objects can modify those objects.
- To protect the data, the object can be locked.
Java is too low-level based on thread-and-lock concurrency, and using locks is a lot of evil, because it is tantamount to having all of the concurrency become queued.
Prior to Java 5, you could use the Synchronized keyword to implement the lock function, which can be used on code blocks and methods to indicate that the thread must obtain the appropriate lock before executing the entire block or method of code. For a non-static method (member method) of a class, this means that it takes a lock on an object instance, and for a static method (class method) of a class, to obtain a class object's lock, and for a synchronous block of code, the programmer can specify the lock to be obtained for that object.
Whether it is a synchronous code block or a synchronous method, only one thread can enter at a time, and if other threads try to enter (whether the same synchronization block or a different synchronization block), the JVM suspends them (into the lock pool). This structure is called the Critical Region (critical section) in the Concurrency theory. Here we can make a summary of the function of synchronized in Java to implement synchronization and lock:
- Only objects can be locked and basic data types cannot be locked
- A single object in an array of locked objects is not locked
- The synchronization method can be considered to contain the entire method of synchronized (this) {...} code block
- The static synchronization method locks its class object
- Internal class synchronization is independent of the external class
- The synchronized modifier is not part of the method signature, so it cannot appear in the method declaration of the interface
- Non-synchronous methods do not care about the state of the locks, they can still be run while the synchronization method is running
- Locks implemented by synchronized are reentrant locks.
Inside the JVM, for efficiency, every thread running at the same time has a cached copy of the data it is processing, and when we use synchronzied for synchronization, it is true that the memory blocks that represent the locked objects in different threads (the replica data remains in sync with the main memory). Now you know why to use the word synchronization, it is simply that after the synchronization block or synchronization method is executed, any changes to the locked object to be written back to the main memory before the lock is released, and after the lock is entered into the synchronization block, the data of the locked object is read from the main memory. The data copy of the thread holding the lock must be synchronized with the data view in main memory.
In the initial version of Java, there is a keyword called volatile, which is a simple synchronous processing mechanism, because volatile-modified variables follow the following rules:
- The value of a variable is always read from the main memory before it is used.
- Changes to the value of a variable are always written back to main memory after completion.
Using the volatile keyword prevents compiler incorrect optimization assumptions in a multithreaded environment (the compiler may optimize variables that do not change values in one thread to constants), but only variables that do not depend on the current state (the value read) should be declared as a volatile variable when modified.
The invariant mode is also a design that can be considered in concurrent programming. The state of the object is constant, and if you want to modify the state of the object, you create a copy of the object and write the changes to the copy without altering the original object, so that there is no state inconsistency, so the immutable object is thread safe. In Java we use the very high frequency string class to adopt this design.
Concurrency Programming for Java 5
Doug Lea offers his monumental masterpiece Java.util.concurrent package in Java 5, which gives Java concurrent programming a lot more choice and a better way to work:
- Executor: The performer of the specific runnable task.Executorservice: A thread pool manager, with a variety of implementation classes, I'll cover some of them. We can submit the runnable,callable to the pool for dispatch.
Semaphore: A count signal volume
Reentrantlock: A reentrant Mutex lock lock, similar in function to synchronized, but much more powerful.
Future: An interface that interacts with the runnable,callable, such as the result of a thread executing after the end of execution, and so on, and also provides a cancel termination thread.
Blockingqueue: Blocks the queue.
Completionservice: Executorservice extension that can get the results of thread execution
Countdownlatch: A synchronous helper class that allows one or more threads to wait until a set of operations that are performed in another thread is completed.
Cyclicbarrier: A synchronization helper class that allows a group of threads to wait until they reach a common barrier point
Future: The future represents the result of an asynchronous calculation.
Scheduledexecutorservice: A executorservice that can be scheduled to run after a given delay or to execute commands on a regular basis.
Next introduce
Executorsnewfixedthreadpool (fixed size thread pool)
Create a thread pool that reuses a collection of fixed threads to run these threads in a shared, unbounded queue (only requests come in and wait for execution in a queue). If any thread terminates due to a failure during execution prior to shutdown, a new thread will perform subsequent tasks (if necessary) instead of it.
Newcachedthreadpool (no boundary pool, automatic thread recovery is possible)
Create a thread pool that can create new threads as needed, but reuse them when previously constructed threads are available. For programs that perform many short-term asynchronous tasks, these thread pools can often improve program performance. Calling execute reuses the previously constructed thread, if the thread is available. If an existing thread is not available, a new thread is created and added to the pool. Terminates and removes from the cache those threads that have not been used for 60 seconds. Therefore, a thread pool that remains idle for a long time does not use any resources. Note that you can use the Threadpoolexecutor construction method to create a thread pool with similar attributes but with different details, such as time-out parameters.
Newsinglethreadexecutor (single background thread)
Create a Executor that uses a single worker thread to run the thread in a unbounded queue. (Note that if this single thread is terminated because of a failure during the execution of the shutdown, a new thread will replace it for subsequent tasks if needed). Each task is guaranteed to be executed sequentially, and no more than one thread is active at any given time. Unlike other equivalent Newfixedthreadpool (1), it is guaranteed to use other threads without reconfiguring the executing program returned by this method.
These methods return the Executorservice object, which is understood to be a thread pool. can also beconstructed directly using Threadpoolexecutor (). You can set the maximum number of threads, the minimum number of threads, and the keepalive time for idle threads, just like a common thread pool. Semaphore a count semaphore. Conceptually, semaphores maintain a license set. If necessary, block each acquire () before the license is available, and then obtain the license. Each release () adds a license that may release a blocked fetch. However, instead of using the actual license object, Semaphore only counts the number of available licenses and takes action accordingly.
Semaphore is typically used to limit the number of threads that can access certain resources (physical or logical). For example, the following class uses semaphores to control access to a content pool:
Here is a practical situation, everyone queued to the toilet, toilets only two locations, to 10 people need to queue.
Import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;import Java.util.concurrent.semaphore;public class Mysemaphore extends Thread {private Semaphore position;private int id; Public Mysemaphore (int i, Semaphore s) {this.id = I;this.position = s;} public void Run () {try {//have no empty toilets if (position.availablepermits () > 0) {System.out.println ("Customer [" + This.id + "] Enter the toilet, there is a vacancy") ;} else {System.out.println ("Customer [" + This.id + "] Enter the toilet, no vacancy, queue");} Get to empty toilet up position.acquire (); System.out.println ("Customer [" + This.id + "] get pit position");//In use ... Thread.Sleep ((int) (Math.random () * 1000)); System.out.println ("Customer [" + This.id + "]");//The toilet is released after the use of Position.release ();} catch (Exception e) {e.printstacktrace ();}} public static void Main (String args[]) {Executorservice list = Executors.newcachedthreadpool (); Semaphore position = new Semaphore (2);//Only two toilets//10 persons for (int i = 0; i <; i++) {List.submit (new Mysemaphore (i + 1, PO sition));} List.shutdown ();p osition.acquireuninterruptibly (2); System.out.println ("Use finished, need to clean up");p osition.release (2);}}
Reentrantlockreentrantlock will be owned by a thread that has recently successfully acquired a lock and has not released the lock. When the lock is not owned by another thread, the thread that called lock succeeds in acquiring the lock and returns. If the current thread already owns the lock, this method will return immediately. You can use the Isheldbycurrentthread () and Getholdcount () methods to check whether this condition occurs.
The construction method of this class accepts an optional fairness parameter.
When set to true, these locks tend to grant access to the longest waiting thread under contention for multiple threads. Otherwise, this lock will not guarantee any particular access order.
Programs that use fair locking behave as low overall throughput (that is, slow, often extremely slow) when accessed by many threads, compared to the default setting (using an unfair lock), but with little difference in obtaining locks and ensuring the equalization of lock allocations. However, it is important to note that fair locking does not guarantee the fairness of thread scheduling. Therefore, one of the many threads that use fair locking can gain multiple chances of success, which occurs when other active threads are not being processed and are not currently holding locks. Also note that the Trylock method does not use the fair setting. This method can be successful, as long as the lock is available, even if other threads are waiting.
It is recommended to always practice immediately, using a try block to invoke lock, in the previous/subsequent constructs, the most typical code is as follows:
Class X {private final reentrantlock lock = new Reentrantlock (),//... public void m () {lock.lock ();//block until Conditi On holdstry {//... method body}finally {Lock.unlock ()}}}
The Blockingqueue supports two additional operations for a queue, both of which wait for the queue to become non-empty when retrieving elements, and wait for space to become available when the element is stored.
Blockingqueue does not accept null elements. Some implementations throw NullPointerException when attempting to add, put, or offer a null element. Null is used as a warning value indicating that the poll operation failed.
The blockingqueue can be of limited capacity. It can have a remainingcapacity at any given time, exceeding this capacity, and cannot put extra elements without blocking.
Blockingqueue that do not have any internal capacity constraints always report the remaining capacity of the integer.max_value.
The Blockingqueue implementation is primarily used by the producer-consumer queue, but it also supports the Collection interface. So, for example, it is possible to use remove (x) to remove any element from the queue.
However, this operation is usually not performed effectively and can only be used on a scheduled occasion, such as when the queued information is canceled.
Blockingqueue implementations are thread-safe. All queueing methods can use internal locking or other forms of concurrency control to automatically achieve their purpose.
However, a large number of Collection operations (AddAll, Containsall, Retainall, and RemoveAll) are not necessary for automatic execution unless specifically stated in the implementation.
So, for example, AddAll (c) might fail (throw an exception) after adding only some of the elements in C.
Blockingqueue essentially does not support the use of any one of the "close" or "shutdown" actions to indicate that no more items are added.
The need for and use of this functionality has a tendency to rely on implementation. For example, a common strategy is to insert special end-of-stream or poison objects for the producer, and interpret them based on the time the consumer obtains them.
The following example shows the basic functionality of this blocking queue.
Import Java.util.concurrent.blockingqueue;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;import Java.util.concurrent.linkedblockingqueue;public class MyBlockingQueue Extends Thread {public static blockingqueue<string> queue = new Linkedblockingqueue<string> (3);p rivate int Index;public myblockingqueue (int i) {this.index = i;} public void Run () {try {queue.put (string.valueof (This.index)); System.out.println ("{" + This.index + "} in queue!");} catch (Exception e) {e.printstacktrace ();}} public static void Main (String args[]) {Executorservice service = Executors.newcachedthreadpool (), for (int i = 0; i < 1 0; i++) {service.submit (new Myblockingqueue (i));} Thread thread = new Thread () {public void run () {try {while (true) {thread.sleep (int) (Math.random () *)); if (Myblock IngQueue.queue.isEmpty ()) break; String str = MyBlockingQueue.queue.take (); SYSTEM.OUT.PRINTLN (str + "has take!");}} catch (Exception e) {e.printstacktrace ();}}}; Service.submit(thread); Service.shutdown ();}}
---------------------Execution Results-----------------
{0} in queue!
{1} in queue!
{2} in queue!
{3} in queue!
0 has take!
{4} in queue!
1 has take!
{6} in queue!
2 has take!
{7} in queue!
3 has take!
{8} in queue!
4 has take!
{5} in queue!
6 has take!
{9} in queue!
7 has take!
8 has take!
5 has take!
9 has take!
-----------------------------------------
Completionservice We now use multithreading in Java, which is usually not directly with the thread object, but instead uses the Executorservice class under the Java.util.concurrent package to initialize a thread pool for our use. I've been used to maintaining a list of the future objects returned by the callable task. Traverse the list in the main thread and call the future's Get () method to fetch the return value of the task. However, in many places I see some code that wraps executorservice through Completionservice and then calls its take () method to fetch the future object. The difference between the two has not been studied before. After reading the source code today, I understand. The main difference between the two is that the submit task does not necessarily follow the list order in which it is maintained.
Each future object that is traversed from the list is not necessarily in the completed state, and the call to get () method is blocked if the system is designed so that after each thread is finished it can continue to do what is behind it, This increases the additional wait time for threads that are behind the list but are completed first. The implementation of Completionservice is to maintain a blockingqueue that holds the future object. Only when this future object state is finished will it be added to this queue, the take () method is actually the consumer in Producer-consumer. It will remove the future object from the queue, and if the queue is empty, it will block there until a completed future object is added to the queue.
Therefore, the first complete must be taken out first. This reduces the unnecessary waiting time. The Executorcompletionservice class provides an implementation of this method.
Import Java.util.concurrent.callable;import Java.util.concurrent.completionservice;import Java.util.concurrent.executorcompletionservice;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;public class Mycompletionservice implements callable<string> {private int id; Public mycompletionservice (int i) {this.id = i;} public static void Main (string[] args) throws Exception {Executorservice service = Executors.newcachedthreadpool (); completionservice<string> completion = new executorcompletionservice<string> (service); for (int i = 0; i < 10; i++) {completion.submit (new Mycompletionservice (i));} for (int i = 0; i < i++) {System.out.println (Completion.take (). get ());} Service.shutdown ();} Public String Call () throws Exception {Integer time = (int) (Math.random () * +); try {System.out.println (this.id + "sta RT "); Thread.Sleep (time); System.out.println (this.id + "End");} catch (Exception e) {e.printstacktrace ();} return this.id + ":" + Time;}}
Countdownlatch a synchronous helper class that allows one or more threads to wait until a set of operations that are executing in another thread is completed.
Initializes the countdownlatch with the given count. Because the countdown () method is called, the await method is blocked until the current count reaches 0.
After that, all the waiting threads are freed, and all subsequent calls to await are returned immediately. This behavior occurs only once-the count cannot be reset. If you need to reset the count, consider using Cyclicbarrier.
Countdownlatch is a universal synchronization tool that has many uses. The Countdownlatch initialized with Count 1 is used as a simple on/Guan device,
Or Portal: All threads that call await are waiting at the entrance until the thread that calls Countdown () opens the portal.
N-initialized Countdownlatch can cause a thread to wait until N threads complete an operation, or to wait until an operation completes N.
A useful feature of Countdownlatch is that it does not require a thread that calls the countdown method to wait until the count arrives 0 o'clock, and before all threads can pass, it simply prevents any thread from continuing through an await.
The following example is written by someone else, very image.
Import Java.util.concurrent.countdownlatch;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;public class Testcountdownlatch {public static void main (string[] args) throws interruptedexception {//Start Countdown lock final Countdownlatch begin = new Countdownlatch (1);//End of Countdown lock final countdownlatch end = new Countdownlatch (10);//10 player final Executorservice exec = executors.newfixedthreadpool (int index = 0; index < 10; index++) {final int NO = index + 1; Runnable run = new Runnable () {public void run () {try {begin.await ();//Always blocks Thread.Sleep ((Long) (Math.random () * 10000)); S Ystem.out.println ("No." + No + "arrived");} catch (Interruptedexception e) {} finally {End.countdown ();}}}; Exec.submit (run);} System.out.println ("Game Start"); Begin.countdown (); end.await (); System.out.println ("Game over"); Exec.shutdown ();}}
Countdownlatch the most important method is countdown () and await (), the former is mainly a countdown, the latter is waiting for the countdown to 0, if not reached 0, only blocking wait.
Cyclicbarrier a synchronization helper class that allows a set of threads to wait for each other until a common barrier point (common barrier points) is reached.
In programs that involve a set of fixed-size threads, these threads have to wait for each other, and cyclicbarrier is useful at this time. Because the barrier can be reused after releasing the waiting thread, it is called a cyclic barrier.
Cyclicbarrier supports an optional Runnable command that runs only once at each barrier point after the last thread in a set of threads arrives (but before releasing all threads). This barrier operation is useful if you update the shared state before continuing with all participating threads.
Example usage: Here is an example of using barrier in a parallel decomposition design, a classic example of a tour:
Import Java.text.simpledateformat;import Java.util.date;import Java.util.concurrent.brokenbarrierexception;import Java.util.concurrent.cyclicbarrier;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;public class Testcyclicbarrier {//Hike required time: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhanprivate static int[] Timewalk = {5, 8, 15, 15, 10};//self-driving tour private static int[] Timeself = {1, 3, 4, 4, 5};//tour bus P Rivate static int[] Timebus = {2, 4, 6, 6, 7};static String now () {SimpleDateFormat SDF = new SimpleDateFormat ("HH:mm:ss "); return Sdf.format (New Date ()) +": ";} Static Class Tour implements Runnable {private int[] times;private cyclicbarrier barrier;private String tourname;public to ur (cyclicbarrier barrier, String tourname, int[] times) {this.times = Times;this.tourname = Tourname;this.barrier = Barrie R;} public void Run () {try {thread.sleep (times[0] * 1000); System.out.println (now () + Tourname + "reached Shenzhen"); barrier.await (); Thread.Sleep (times[1] * 1000); System.out.println (now () + Tourname + "reached Guangzhou"); barrier.await (); Thread.Sleep (times[2] * 1000); System.out.println (now () + Tourname + "reached Shaoguan"); barrier.await (); Thread.Sleep (times[3] * 1000); System.out.println (now () + Tourname + "reached Changsha"); barrier.await (); Thread.Sleep (times[4] * 1000); System.out.println (now () + Tourname + "reached Wuhan"); barrier.await ();} catch (Interruptedexception e) {} catch (Brokenbarrierexception e) {}}}public static void main (string[] args) {//three tours CYC Licbarrier barrier = new Cyclicbarrier (3); Executorservice exec = Executors.newfixedthreadpool (3); Exec.submit (new tour (barrier, "Walktour", Timewalk)); Exec.submit (new tour (barrier, "Selftour", timeself));//When we comment on the code below, we will find that the program is blocked and cannot continue running. Exec.submit (new tour (barrier, "Bustour", Timebus)); Exec.shutdown ();}}
The most important attribute of Cyclicbarrier is the number of participants, and the best way to do this is to await (). After all the threads have called await (), it means that the threads can continue to execute, or they will wait.
Futurefuture represents the result of an asynchronous calculation. It provides a way to check whether the calculation is complete, to wait for the completion of the calculation, and to retrieve the results of the calculation.
You can only use the Get method to retrieve the results after the calculation is complete and, if necessary, block this method before the calculation is complete. Cancellation is performed by the Cancel method.
Other methods are provided to determine whether the task is completed properly or canceled. Once the calculation is complete, you can no longer cancel the calculation.
If you use the future for the sake of cancellation and do not provide the available results, you can declare the Future<?> form type and return null as the result of the underlying task.
This we have seen in front of completionservice, this future function, and this can be specified as a return object at the time of the commit thread. Look at the following example:Import Java.util.concurrent.callable;import Java.util.concurrent.executionexception;import Java.util.concurrent.executorservice;import Java.util.concurrent.executors;import java.util.concurrent.Future; public class Myfuturetask {/** * @param args * @throws interruptedexception * @throws executionexception * @throws interr Uptedexception * @throws executionexception */public static void Main (string[] args) throws Interruptedexception, Executi onexception {final Executorservice exe=executors.newfixedthreadpool (3); Callable<string> call=new callable<string> () {public String call () throws Interruptedexception {return " Thread is finished ";}}; Future<string> Task=exe.submit (call); String Obj=task.get (); SYSTEM.OUT.PRINTLN (obj+ "process End"); SYSTEM.OUT.PRINTLN ("Total process End"); Exe.shutdown ();}} Class Mythreadtest implements Runnable {private string str;public mythreadtest (String str) {this.str = str;} public void Run () {this.setstr ("Allen" +str); public void addstring (String str) {this.str = "Allen: "+ str;} Public String Getstr () {return str;} public void Setstr (String str) {this.str = str;}}
Scheduledexecutorservice a executorservice that can be scheduled to run after a given delay or to execute commands on a regular basis.
The schedule method creates a task with various delays and returns a task object that can be used to cancel or check execution. The Scheduleatfixedrate and Schedulewithfixeddelay methods Create and perform certain tasks that run regularly before they are canceled.
The order submitted with the Submit method of Executor.execute (java.lang.Runnable) and Executorservice is arranged by the requested 0 delay.
0 and negative delay (but not cycles) are allowed in the schedule method, and these are treated as a request for immediate execution.
All schedule methods accept relative delays and periods as parameters, rather than absolute time or date. It is easy to convert the absolute time represented by date into the required form.
For example, to schedule a later date to run, you can use: Schedule (Task, Date.gettime ()-System.currenttimemillis (), timeunit.milliseconds).
Note, however, that due to network time synchronization protocol, clock drift, or other factors, the expiration date of the relative delay does not have to match the current date of the enabled task.
The Executors class provides a convenient factory approach to the Scheduledexecutorservice implementations provided in this package.
The following examples are also popular online.
import static Java.util.concurrent.timeunit.seconds;import Java.util.date;import Java.util.concurrent.executors;import Java.util.concurrent.scheduledexecutorservice;import Java.util.concurrent.scheduledfuture;public class Testscheduledthread {public static void main (string[] args) {final Scheduledexecutorservice Scheduler = Executors.newscheduledthreadpool (2), final Runnable beeper = new Runnable () {int Count = 0;public void Run () {System.out.println (new Date () + "beep" + (++count));}};/ /1 seconds to run, and run once every 2 seconds final scheduledfuture Beeperhandle = Scheduler.scheduleatfixedrate (beeper, 1, 2, SECONDS);//2 seconds to run, And then re-run the final scheduledfuture beeperHandle2 = Scheduler.schedulewithfixeddelay (beeper, 2, 5, SECONDS) each time after the last task has finished running for 5 seconds;// Closes the task after 30 seconds and closes Schedulerscheduler.schedule (new Runnable () {public void run () {Beeperhandle.cancel (true); Beeperhandle2.cancel (True); Scheduler.shutdown ();}}, +, SECONDS);}}
In addition, the following improvements are available:
1. A variety of containers for specific purposes, facilitating communication between threads. such as Blockingqueue,delayqueue,concurrenthashmap,copyonwritearraylist and so on.
2. Thread-Safe basic variable classes are provided in package java.util.concurrent.atomic.
3. Timing. The Timeunit class provides multiple granularity (including nanosecond levels) for specifying and controlling timeout-based operations. To replace the humble thread.sleep.
Java Concurrency 6-summary