We are pleased to present Alibaba Java Coding Guidelines, which consolidates the best programming practices from Alibaba Group's technical teams. A vast number of Java programming teams impose demanding requirements on code quality across projects as we encourage reuse and better understanding of each other's programs. We have seen many programming problems in the past. For example, defective database table structures and index designs may cause software architecture flaws and performance risks. As another example, confusing code structures make it difficult to maintain. Furthermore, vulnerable code without authentication is prone to hackers’ attacks. To address those kinds of problems, we developed this document for Java developers in Alibaba.
This document consists of five parts: Programming Specification, Exception and Logs, MySQL Specification, Project Specification and Security Specification. Based on the severity of the concerns, each specification is classified into three levels: Mandatory, Recommended and Reference. Further clarification is expressed in:
(1) "Description", which explains the content;
(2) "Positive examples", which describe recommended coding and implementation approaches;
(3) "Counter examples", which describe precautions and actual error cases.
The main purpose of this document is to help developers improve code quality. As a result, developers can minimize potential and repetitive code errors. In addition, specification is essential to modern software architectures, which enable effective collaborations. As an analogy, traffic regulations are intended to protect public safety rather than to deprive the rights of driving. It is easy to imagine the chaos of traffic without speed limits and traffic lights. Instead of destroying the creativity and elegance of program, the purpose of developing appropriate specification and standards of software is to improve the efficiency of collaboration by limiting excessive personalization.
We will continue to collect feedback from the community to improve Alibaba Java Coding Guidelines.
1. Programming Specification
Naming Conventions
1. [Mandatory] Names should not start or end with an underline or a dollar sign.
2. [Mandatory] Using Chinese, Pinyin, or Pinyin-English mixed spelling in naming is strictly prohibited. Accurate English spelling and grammar will make the code readable, understandable, and maintainable.
Positive example: alibaba / taobao / youku / Hangzhou. In these cases, Chinese proper names in Pinyin are acceptable.
3. [Mandatory] Class names should be nouns in UpperCamelCase except domain models: DO, BO, DTO, VO, etc.
5. [Mandatory] Constant variable names should be written in upper characters separated by underscores. These names should be semantically complete and clear.
6. [Mandatory] Abstract class names must start with Abstract or Base. Exception class names must end with Exception. Test case names shall start with the class names to be tested and end with Test.
7. [Mandatory] Brackets are a part of an Array type. The definition could be: String[] args;
Counter example: String args[];
8. [Mandatory] Do not add 'is' as prefix while defining Boolean variable, since it may cause a serialization exception in some Java frameworks.
Counter example: boolean isSuccess; The method name will be isSuccess() and then RPC framework will deduce the variable name as 'success', resulting in a serialization error since it cannot find the correct attribute.
9. [Mandatory] A package should be named in lowercase characters. There should be only one English word after each dot. Package names are always in singular format while class names can be in plural format if necessary.
Positive example: com.alibaba.open.util can be used as a package name for utils;
MessageUtils can be used as a class name.
10. [Mandatory] Uncommon abbreviations should be avoided for the sake of legibility.
11. [Recommended] The pattern name is recommended to be included in the class name if any design pattern is used.
Positive example: public class OrderFactory; public class LoginProxy;public class ResourceObserver;
Note: Including corresponding pattern names helps readers understand ideas in design patterns quickly.
12. [Recommended] Do not add any modifier, including public, to methods in interface classes for coding simplicity. Please add valid Javadoc comments for methods. Do not define any variables in the interface except for the common constants of the application.
Positive example: method definition in the interface: void f();
constant definition: String COMPANY = "alibaba";
Note: In JDK8 it is allowed to define a default implementation for interface methods, which is valuable for all implemented classes.
13. There are two main rules for interface and corresponding implementation class naming:
1) [Mandatory] All Service and DAO classes must be interfaces based on SOA principle. Implementation class names should end with Impl.
Positive example: CacheServiceImpl to implement CacheService.
2) [Recommended] If the interface name is to indicate the ability of the interface, then its name should be an adjective.
Positive example: AbstractTranslator to implement Translatable.
14. [For Reference] An Enumeration class name should end with Enum. Its members should be spelled out in upper case words, separated by underlines.
Note: Enumeration is indeed a special constant class and all constructor methods are private by default.
Positive example: Enumeration name: DealStatusEnum; Member name: SUCCESS / UNKOWN_REASON.
15. [For Reference] Naming conventions for different package layers:
A) Naming conventions for Service/DAO layer methods
1) Use get as name prefix for a method to get a single object.
2) Use list as name prefix for a method to get multiple objects.
3) Use count as name prefix for a statistical method.
4) Use insert or save (recommended) as name prefix for a method to save data.
5) Use delete or remove (recommended) as name prefix for a method to remove data.
6) Use update as name prefix for a method to update data.
B) Naming conventions for Domain models
1) Data Object: *DO, where * is the table name.
2) Data Transfer Object: *DTO, where * is a domain-related name.
3) Value Object: *VO, where * is a website name in most cases.
4) POJO generally point to DO/DTO/BO/VO but cannot be used in naming as *POJO.
Constant Conventions
1. [Mandatory] Magic values, except for predefined, are forbidden in coding.
2. [Mandatory] 'L' instead of 'l' should be used for long or Long variable because 'l' is easily to be regarded as number 1 in mistake.
Counter example: Long a = 2l, it is hard to tell whether it is number 21 or Long 2.
3. [Recommended] Constants should be placed in different constant classes based on their functions. For example, cache related constants could be put in CacheConsts while configuration related constants could be kept in ConfigConsts.
Note: It is difficult to find one constant in one big complete constant class.
4. [Recommended] Constants can be shared in the following 5 different layers: shared in multiple applications; shared inside an application; shared in a sub-project; shared in a package; shared in a class.
1) Shared in multiple applications: keep in a library, under constant directory in client.jar;
2) Shared in an application: keep in shared modules within the application, under constant directory;
Counter example: Obvious variable names should also be defined as common shared constants in an application. The following definitions caused an exception in the production environment: it returns false, but is expected to return true for A.YES.equals(B.YES).
Definition in Class A: public static final String YES = "yes";
Definition in Class B: public static final String YES = "y";
3) Shared in a sub-project: placed under constant directory in the current project;
4) Shared in a package: placed under constant directory in current package;
5) Shared in a class: defined as 'private static final' inside class.
5. [Recommended] Use an enumeration class if values lie in a fixed range or if the variable has attributes. The following example shows that extra information (which day it is) can be included in enumeration:
1. [Mandatory] Rules for braces. If there is no content, simply use {} in the same line. Otherwise:
1) No line break before the opening brace.
2) Line break after the opening brace.
3) Line break before the closing brace.
4) Line break after the closing brace, only if the brace terminates a statement or terminates a method body, constructor or named class. There is no line break after the closing brace if it is followed by else or a comma.
2. [Mandatory] No space is used between the '(' character and its following character. Same for the ')' character and its preceding character. Refer to the Positive Example at the 5th rule.
3. [Mandatory] There must be one space between keywords, such as if/for/while/switch, and parentheses.
4. [Mandatory] There must be one space at both left and right side of operators, such as '=', '&&', '+', '-', ternary operator, etc.
5. [Mandatory] Each time a new block or block-like construct is opened, the indent increases by four spaces. When the block ends, the indent returns to the previous indent level. Tab characters are not used for indentation.
Note: To prevent tab characters from being used for indentation, you must configure your IDE. For example, "Use tab character" should be unchecked in IDEA, "insert spaces for tabs" should be checked in Eclipse.
Positive example:
publicstaticvoidmain(String[] args) {
// four spaces indentStringsay = "hello";
// one space before and after the operatorintflag = 0;
// one space between 'if' and '('; // no space between '(' and 'flag' or between '0' and ')'if (flag == 0) {
System.out.println(say);
}
// one space before '{' and line break after '{'if (flag == 1) {
System.out.println("world");
// line break before '}' but not after '}' if it is followed by 'else'
} else {
System.out.println("ok");
// line break after '}' if it is the end of the block
}
}
6. [Mandatory] Java code has a column limit of 120 characters. Except import statements, any line that would exceed this limit must be line-wrapped as follows:
1) The second line should be intented at 4 spaces with respect to the first one. The third line and following ones should align with the second line.
2) Operators should be moved to the next line together with following context.
3) Character '.' should be moved to the next line together with the method after it.
4) If there are multiple parameters that extend over the maximum length, a line break should be inserted after a comma.
5) No line breaks should appear before parentheses.
Positive example:
StringBuffersb = newStringBuffer();
// line break if there are more than 120 characters, and 4 spaces indent at// the second line. Make sure character '.' moved to the next line // together. The third and fourth lines are aligned with the second one. sb.append("zi").append("xin").
.append("huang")...
.append("huang")...
.append("huang");
Counter example:
StringBuffersb = newStringBuffer();
// no line break before '('sb.append("zi").append("xin")...append
("huang");
// no line break before ',' if there are multiple paramsinvoke(args1, args2, args3, ...
, argsX);
7. [Mandatory] There must be one space between a comma and the next parameter for methods with multiple parameters.
Positive example: One space is used after the ',' character in the following method definition.
f("a", "b", "c");
8. [Mandatory] The charset encoding of text files should be UTF-8 and the characters of line breaks should be in Unix format, instead of Windows format.
9. [Recommended] It is unnecessary to align variables by several spaces.
Note: It is cumbersome to insert several spaces to align the variables above.
10. [Recommended] Use a single blank line to separate sections with the same logic or semantics.
Note: It is unnecessary to use multiple blank lines to do that.
OOP Rules
1. [Mandatory] A static field or method should be directly referred to by its class name instead of its corresponding object name.
2. [Mandatory] An overridden method from an interface or abstract class must be marked with @Override annotation.
Counter example: For getObject() and get0bject(), the first one has a letter 'O', and the second one has a number '0'. To accurately determine whether the overriding is successful, an @Override annotation is necessary. Meanwhile, once the method signature in the abstract class is changed, the implementation class will report a compile-time error immediately.
3. [Mandatory]varargs is recommended only if all parameters are of the same type and semantics. Parameters with Object type should be avoided.
Note: Arguments with the varargs feature must be at the end of the argument list. (Programming with the varargs feature is not recommended.)
Positive example:
publicUsergetUsers(Stringtype, Integer... ids);
4. [Mandatory] Modifying the method signature is forbidden to avoid affecting the caller. A @Deprecated annotation with an explicit description of the new service is necessary when an interface is deprecated.
5. [Mandatory] Using a deprecated class or method is prohibited.
Note: For example, decode(String source, String encode) should be used instead of the deprecated method decode(String encodeStr). Once an interface has been deprecated, the interface provider has the obligation to provide a new one. At the same time, client programmers have the obligation to use the new interface.
6. [Mandatory] Since NullPointerException can possibly be thrown while calling the equals method of Object, equals should be invoked by a constant or an object that is definitely not null.
Positive example: "test".equals(object);
Counter example: object.equals("test");
Note: java.util.Objects#equals (a utility class in JDK7) is recommended.
7. [Mandatory] Use the equals method, rather than reference equality ==, to compare primitive wrapper classes.
Note: Consider this assignment: Integer var = ?. When it fits the range from -128 to 127, we can use == directly for a comparison. Because the Integer object will be generated by IntegerCache.cache, which reuses an existing object. Nevertheless, when it fits the complementary set of the former range, the Integer object will be allocated in the heap, which does not reuse an existing object. This is a pitfall. Hence the equals method is recommended.
8. [Mandatory] To judge the equivalence of floating-point numbers, == cannot be used for primitive types, while equals cannot be used for wrapper classes.
Note: Floating-point numbers are composed by mantissa and exponent, which is similar to the coefficient and exponent of scientific notation. Most decimal fractions cannot be represented precisely by binary.
Counter example:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
if (a == b) {
// seems like this block will be executed
// but the result of a == b is false
}
Float x = Float.valueOf(a);
Float y = Float.valueOf(b);
if (x.equals(y)) {
// seems like this block will be executed
// but the result of x.equals(y) is false
}
Positive example:
(1) Specify an error range. Consider two floating-pointing numbers as equal if the difference between them is within this range.
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f;
if (Math.abs(a - b) < diff) {
System.out.println("true");
}
(2) Use BigDecimal to operate.
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
System.out.println("true");
}
9. [Mandatory] Rules for using primitive data types and wrapper classes:
1) Members of a POJO class must be wrapper classes.
2) The return value and arguments of a RPC method must be wrapper classes.
3) [Recommended] Local variables should be primitive data types.
Note: In order to remind the consumer of explicit assignments, there are no initial values for members in a POJO class. As a consumer, you should check problems such as NullPointerException and warehouse entries for yourself.
Positive example: As the result of a database query may be null, assigning it to a primitive date type will cause a risk of NullPointerException because of autoboxing.
Counter example: Consider the output of a transaction volume's amplitude, like ±x%. As a primitive data, when it comes to a failure of calling a RPC service, the default return value: 0% will be assigned, which is not correct. A hyphen like - should be assigned instead. Therefore, the null value of a wrapper class can represent additional information, such as a failure of calling a RPC service, an abnormal exit, etc.
10. [Mandatory] While defining POJO classes like DO, DTO, VO, etc., do not assign any default values to the members.
11. [Mandatory] To avoid a deserialization failure, do not change the serialVersionUID when a serialized class needs to be updated, such as adding some new members. If a completely incompatible update is needed, change the value of serialVersionUID in case of a confusion when deserialized.
Note: The inconsistency of serialVersionUID may cause an InvalidClassException at runtime.
12. [Mandatory] Business logic in constructor methods is prohibited. All initializations should be implemented in the init method.
13. [Mandatory] The toString method must be implemented in a POJO class. The super.toString method should be called in in the beginning of the implementation if the current class extends another POJO class.
Note: We can call the toString method in a POJO directly to print property values in order to check the problem when a method throws an exception in runtime.
14. [Recommended] When accessing an array generated by the split method in String using an index, make sure to check the last separator whether it is null to avoid IndexOutOfBoundException.
Note:
Stringstr = "a,b,c,,";
String[] ary = str.split(",");
// The expected result exceeds 3. However it turns out to be 3.System.out.println(ary.length);
15. [Recommended] Multiple constructor methods or homonymous methods in a class should be put together for better readability.
16. [Recommended] The order of methods declared within a class is: public or protected methods -> private methods -> getter/setter methods.
Note: As the most concerned ones for consumers and providers, public methods should be put on the first screen. Protected methods are only cared for by the subclasses, but they have chances to be vital when it comes to Template Design Pattern. Private methods, the black-box approaches, basically are not significant to clients. Getter/setter methods of a Service or a DAO should be put at the end of the class implementation because of the low significance.
17. [Recommended] For a setter method, the argument name should be the same as the field name. Implementations of business logics in getter/setter methods, which will increase difficulties of the troubleshooting, are not recommended.
18. [Recommended] Use the append method in StringBuilder inside a loop body when concatenating multiple strings.
Counter example:
Stringstr = "start";
for (inti = 0; i < 100; i++) {
str = str + "hello";
}
Note: According to the decompiled bytecode file, for each loop, it allocates a StringBuilder object, appends a string, and finally returns a String object via the toString method. This is a tremendous waste of memory.
19. [Recommended] Keyword final should be used in the following situations:
1) A class which is not allow to be inherited, or a local variable not to be reassigned.
2) An argument which is not allow to be modified.
3) A method which is not allow to be overridden.
20. [Recommended] Be cautious to copy an object using the clone method in Object.
Note: The default implementation of clone in Object is a shallow (not deep) copy, which copies fields as pointers to the same objects in memory.
21. [Recommended] Define the access level of members in class with severe restrictions:
1) Constructor methods must be private if an allocation using new keyword outside of the class is forbidden.
2) Constructor methods are not allowed to be public or default in a utility class.
3) Nonstatic class variables that are accessed from inheritants must be protected.
4) Nonstatic class variables that no one can access except the class that contains them must be private.
5) Static variables that no one can access except the class that contains them must be private.
6) Static variables should be considered in determining whether they are final.
7) Class methods that no one can access except the class that contains them must be private.
8) Class methods that are accessed from inheritants must be protected.
Note: We should strictly control the access for any classes, methods, arguments and variables. Loose access control causes harmful coupling of modules. Imagine the following situations. For a private class member, we can remove it as soon as we want. However, when it comes to a public class member, we have to think twice before any updates happen to it.
Collection
1. [Mandatory] The usage of hashCode and equals should follow:
1) Override hashCode if equals is overridden.
2) These two methods must be overridden for elements of a Set since they are used to ensure that no duplicate object will be inserted in Set.
3) These two methods must be overridden for any object that is used as the key of Map.
Note: String can be used as the key of Map since String defines these two methods.
2. [Mandatory] Do not add elements to collection objects returned by keySet()/values()/entrySet(), otherwise UnsupportedOperationException will be thrown.
3. [Mandatory] Do not add nor remove to/from immutable objects returned by methods in Collections, e.g. emptyList()/singletonList().
Counter example: Adding elements to Collections.emptyList() will throw UnsupportedOperationException.
4. [Mandatory] Do not cast subList in class ArrayList, otherwise ClassCastException will be thrown: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
Note: subList of ArrayList is an inner class, which is a view of ArrayList. All operations on the Sublist will affect the original list.
5. [Mandatory] When using subList, be careful when modifying the size of original list. It might cause ConcurrentModificationException when performing traversing, adding or deleting on the subList.
6. [Mandatory] Use toArray(T[] array) to convert a list to an array. The input array type should be the same as the list whose size is list.size().
Counter example: Do not use toArray method without arguments. Since the return type is Object[], ClassCastException will be thrown when casting it to a different array type.
Positive example:
Note: When using toArray method with arguments, pass an input with the same size as the list. If input array size is not large enough, the method will re-assign the size internally, and then return the address of new array. If the size is larger than needed, extra elements (index[list.size()] and later) will be set to null.
7. [Mandatory] Do not use methods which will modify the list after using Arrays.asList to convert array to list, otherwise methods like add/remove/clear will throw UnsupportedOperationException.
Note: The result of asList is the inner class of Arrays, which does not implement methods to modify itself. Arrays.asList is only a transferred interface, data inside which is stored as an array.
Case 1: list.add("c"); will throw a runtime exception.
Case 2: str[0]= "gujin";list.get(0) will be modified.
8. [Mandatory] Method add cannot be used for generic wildcard with <? Extends T>, method get cannot be used with <? super T>, which probably goes wrong.
Note: About PECS (Producer Extends Consumer Super) principle:
Extends is suitable for frequently reading scenarios.
Super is suitable for frequently inserting scenarios.
9. [Mandatory] Do not remove or add elements to a collection in a foreach loop. Please use Iterator to remove an item. Iterator object should be synchronized when executing concurrent operations.
Counter example:
List<String> a = newArrayList<String>();
a.add("1");
a.add("2");
for (Stringtemp : a) {
if ("1".equals(temp)){
a.remove(temp);
}
}
Note: If you try to replace "1" with "2", you will get an unexpected result.
Positive example:
Iterator<String> it = a.iterator();
while (it.hasNext()) {
Stringtemp = it.next();
if (deletecondition) {
it.remove();
}
}
10. [Mandatory] In JDK 7 and above version, Comparator should meet the three requirements listed below, otherwise Arrays.sort and Collections.sort will throw IllegalArgumentException.
Note:
1) Comparing x,y and y,x should return the opposite result.
2) If x>y and y>z, then x>z.
3) If x=y, then comparing x with z and comparing y with z should return the same result.
Counter example: The program below cannot handle the case if o1 equals to o2, which might cause an exception in a real case:
11. [Recommended] Set a size when initializing a collection if possible.
Note: Better to use ArrayList(int initialCapacity) to initialize ArrayList.
12. [Recommended] Use entrySet instead of keySet to traverse KV maps.
Note: Actually, keySet iterates through the map twice, firstly convert to Iterator object, then get the value from the HashMap by key. EntrySet iterates only once and puts keys and values in the entry which is more efficient. Use Map.foreach method in JDK8.
Positive example: values() returns a list including all values, keySet() returns a set including all values, entrySet() returns a k-v combined object.
13. [Recommended] Carefully check whether a k/v collection can store null value, refer to the table below:
Collection
Key
Value
Super
Note
Hashtable
Null is not allowed
Null is not allowed
Dictionary
Thread-safe
ConcurrentHashMap
Null is not allowed
Null is not allowed
AbstractMap
Segment lock
TreeMap
Null is not allowed
Null is allowed
AbstractMap
Thread-unsafe
HashMap
Null is allowed
Null is allowed
AbstractMap
Thread-unsafe
Counter example: Confused by HashMap, lots of people think null is allowed in ConcurrentHashMap. Actually, NullPointerException will be thrown when putting in null value.
14. [For Reference] Properly use sort and order of a collection to avoid negative influence of unsorted and unordered one.
Note: Sorted means that its iteration follows specific sorting rule. Ordered means the order of elements in each traverse is stable. e.g. ArrayList is ordered and unsorted, HashMap is unordered and unsorted, TreeSet is ordered and sorted.
15. [For Reference] Deduplication operations could be performed quickly since set stores unique values only. Avoid using method contains of List to perform traverse, comparison and de-duplication.
Concurrency
1. [Mandatory] Thread-safe should be ensured when initializing singleton instance, as well as all methods in it.
Note: Resource driven class, utility class and singleton factory class are all included.
2. [Mandatory] A meaningful thread name is helpful to trace the error information, so assign a name when creating threads or thread pools.
3. [Mandatory] Threads should be provided by thread pools. Explicitly creating threads is not allowed.
Note: Using thread pool can reduce the time of creating and destroying thread and save system resource. If we do not use thread pools, lots of similar threads will be created which lead to "running out of memory" or over-switching problems.
4. [Mandatory] A thread pool should be created by ThreadPoolExecutor rather than Executors. These would make the parameters of the thread pool understandable. It would also reduce the risk of running out of system resource.
Note: Below are the problems created by usage of Executors for thread pool creation:
1) FixedThreadPool and SingleThreadPool:
Maximum request queue size Integer.MAX_VALUE. A large number of requests might cause OOM.
2) CachedThreadPool and ScheduledThreadPool:
The number of threads which are allowed to be created is Integer.MAX_VALUE. Creating too many threads might lead to OOM.
5. [Mandatory]SimpleDateFormat is unsafe, do not define it as a static variable. If have to, lock or DateUtils class must be used.
Positive example: Pay attention to thread-safety when using DateUtils. It is recommended to use as below:
Note: In JDK8, Instant can be used to replace Date, Calendar is replaced by LocalDateTime, Simpledateformatter is replaced by DateTimeFormatter.
6. [Mandatory]remove() method must be implemented by ThreadLocal variables, especially when using thread pools in which threads are often reused. Otherwise, it may affect subsequent business logic and cause unexpected problems such as memory leak.
7. [Mandatory] In highly concurrent scenarios, performance of Lock should be considered in synchronous calls. A block lock is better than a method lock. An object lock is better than a class lock.
8. [Mandatory] When adding locks to multiple resources, tables in the database and objects at the same time, locking sequence should be kept consistent to avoid deadlock.
Note: If thread 1 does update after adding lock to table A, B, C accordingly, the lock sequence of thread 2 should also be A, B, C, otherwise deadlock might happen.
9. [Mandatory] When getting a lock by blocking methods, such as waiting in the blocking queue, lock() implemented from Lock must be put outside the try block. Besides, make sure no method between the lock() and try block in case that the lock won't be released in the finally block.
Note 1: If there are some exceptions thrown between lock() and try block, it won't be able to release the lock, causing that the other threads cannot get the lock.
Note 2: If lock() fails to run successfully between try block and lock(), it is possible that unlock() won't work. Then AQS(AbstractQueuedSynchronizer) method will be called (depends on the implementation of the class) and IllegalMonitorStateException will be thrown.
Note 3: It is possible that when implementing the lock() method in Lock object, it would throw unchecked Exception, resulting in the same outcome as Note 2.
10. [Mandatory] A lock needs to be used to avoid update failure when modifying one record concurrently. Add lock either in application layer, in cache, or add optimistic lock in the database by using version as update stamp.
Note: If access confliction probability is less than 20%, recommend to use optimistic lock, otherwise use pessimistic lock. Retry number of optimistic lock should be no less than 3.
11. [Mandatory] Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions.
12. [Recommended] When using CountDownLatch to convert asynchronous operations to synchronous ones, each thread must call countdown method before quitting. Make sure to catch any exception during thread running, to let countdown method be executed. If main thread cannot reach await method, program will return until timeout.
Note: Be careful, exception thrown by sub-thread cannot be caught by main thread.
13. [Recommended] Avoid using Random instance by multiple threads. Although it is safe to share this instance, competition on the same seed will dam
请发表评论