One, multithreading problems and simple optimization
public class ThreadTest1 { public static void main(String[] args) { new Producer().start(); new Consumer().start(); } static class ProductObject { public static String value = null; } static class Consumer extends Thread { @Override public void run() { while (true) { if (ProductObject.value ! = null) {system.out.println (" consumer "+ productobject.value); ProductObject.value = null; }}}} static class Producer extends Thread {@override public void run() {// While (true) {if (productobject. value == null) {productobject. value = "No: "+ system.currentTimemillis (); System.out.println(" produce product "+ productobject. value); } } } } }Copy the code
Result output:
Production product No: 1505980855609 Consumer product No: 1505980855609 Consumer product No: 1505980855609 Consumer product No: 1505980855609 Consumer product No: 1505980855609 Consumer product No: 1505980855609 Consumer product No: 1505980855609 1505980855609 Production products No: 1505980855609 consumer products No: 1505980855609 Production products No: 1505980855609 Production products No: 1505980855609 Production products No: 1505980855609 1505980855609 Consumer Product No: 1505980855609 Production product No: 1505980855609 Consumer product No: 1505980855609 Production product No: 1505980855609Copy the code
We found that the example did not execute all the way, but executed for a while and then stopped printing
Reason 1.
When multiple threads access a member variable, each thread gets a copy of the variable stored and computed on its own stack for speed. But then there’s the synchronization problem. When a thread changes the value of its stack copy and does not immediately synchronize it to main memory, another thread retrieves the variable from main memory and gets expired data.
1. Solutions
To solve this problem, synchronized can be used to synchronize operations on the variable, or the variable can be declared as a volatile object using the volatile keyword so that each thread does not create a copy to its own stack and instead operates directly on main memory.
(1) volatile
Add volatile to the object/variable. A Volatile variable forces the value of the member variable to be re-read from shared memory each time it is accessed by a thread. Moreover, when a member variable changes, threads are forced to write the changed value back to shared memory. This way, at any time, two different threads will always see the same value of a member variable. The Java language specification states that, for best speed, threads are allowed to keep a private copy of a shared member variable and compare it to the original value only when the thread enters or leaves a synchronized code block. When multiple threads interact with an object at the same time, attention must be taken to keep the threads informed of changes in shared member variables. The volatile keyword tells the JVM not to keep a private copy of the member variable, but to interact directly with the shared member variable. Usage suggestion: Use volatile on member variables accessed by two or more threads. Do not use it when the variable to be accessed is already in a synchronized block or is a constant. Using volatile is inefficient because it blocks out necessary code optimizations in the JVM, so use this keyword only when necessary.
static class ProductObject {
public volatile static String value = null; }}Copy the code
Result output:
Consumer products No: 1505982581204 Manufactured products No: 1505982581204 Manufactured products No: 1505982581204 manufactured products No: 1505982581204 manufactured products No: 1505982581204 Manufactured products No: 1505982581204 Manufactured products 1505982581204 Consumer Product No: 1505982581204 Manufactured product No: 1505982581204 Manufactured product No: 1505982581204Copy the code
The program has been output to meet the requirements
(2) the synchronized
In the example above, the performance cost of volatile while execution is high, and synchronization is added to avoid large performance cost
Add the synchronized modifier to the object/variable. In threads, use synchronized methods or synchronized blocks.
public class ThreadTest1 {
public static void main(String[] args) {
Object lock = new Object();
new Producer(lock).start();
new Consumer(lock).start();
}
static class ProductObject {
public static String value = null;
}
static class Consumer extends Thread {
Object lock;
public Consumer(Object lock) {
this.lock = lock;
}
@Override
public void run(a) {
while (true) {
synchronized (lock) {/ / the mutex
if(ProductObject.value ! =null) {
System.out.println("Consumer products" + ProductObject.value);
ProductObject.value = null;
}
}
}
}
}
static class Producer extends Thread {
Object lock;
public Producer(Object lock) {
this.lock = lock;
}
@Override
public void run(a) {
// Keep producing products
while (true) {
synchronized (lock) {/ / the mutex
if (ProductObject.value == null) {
// The product has been consumed and new products are produced
ProductObject.value = "No:" + System.currentTimeMillis();
System.out.println("Produce the product" + ProductObject.value);
}
}
}
}
}
}
Copy the code
The program has been output to meet the requirements
However, wait() notify() is introduced in order to determine the order in which an object lock is executed (reducing the number of polls)
Obj.wait(), with obj.notify (), must be used with synchronized(Obj). Synchronized (Obj){Obj (Obj){Obj (Obj){Obj (Obj); } statement block. To wait, a thread acquires an object lock, releases the object lock, and sleeps. Until another thread calls notify() of the object to wake it up, the object lock is acquired and execution continues. The corresponding notify() is the operation to wake up the object lock. Synchronized (){}. The JVM automatically releases the lock on a random thread, assigns the lock to the thread, wakes it up, and continues execution. This provides synchronous, wake up operations across threads. Thread.sleep() and Object.wait() both suspend the current Thread and release CPU control. The main difference is that Object.wait() releases CPU control while releasing the Object lock.
Optimized program:
public class ThreadTest1 {
/ / product
static class ProductObject{
// Thread operation variables visible
public static String value;
}
// The producer thread
static class Producer extends Thread{
Object lock;
public Producer(Object lock) {
this.lock = lock;
}
@Override
public void run(a) {
// Keep producing products
while(true) {synchronized (lock) { / / the mutex
// The product has not been consumed yet, wait
if(ProductObject.value ! =null) {try {
lock.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}// The product has been consumed and new products are produced
ProductObject.value = "NO:"+System.currentTimeMillis();
System.out.println("Production products:"+ProductObject.value);
lock.notify(); // The production is completed, and the consumer is notified to consume}}}}// Consumer thread
static class Consumer extends Thread{
Object lock;
public Consumer(Object lock) {
this.lock = lock;
}
@Override
public void run(a) {
while(true) {synchronized (lock) {
// There is no product to consume
if(ProductObject.value == null) {// Wait, block
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumer Products:"+ProductObject.value);
ProductObject.value = null;
lock.notify(); // Complete consumption, notify the producer, continue production}}}}public static void main(String[] args) {
Object lock = new Object();
new Producer(lock).start();
newConsumer(lock).start(); }}Copy the code
(4) The difference between volatile and synchronized
1) Volatile essentially tells the JVM that the value of the current variable in the register is uncertain and needs to be read from main memory, whereas synchronized locks the current variable so that only the current thread can access it and other threads are blocked. 2) Volatile can only be used at the variable level, whereas synchronized can only be used in variables and methods. 3) Volatile can only be used for change visibility, whereas synchronized can be used for change visibility and atomicity.
In The Java Ideas for Programming, atomicity is achieved by using volatile when defining long or double variables (simple assignment and return operations). 4) Volatile does not block a thread, whereas synchronized does.
5) Volatile does not work when the value of a field depends on its previous value, as n=n+1,n++, etc. Volatile will not work if the value of one field is limited by the value of another field, such as the lower and upper boundaries of the Range class, which must follow the lower<=upper limit.
6) The only safe time to use volatile instead of synchronized is when there is only one mutable field in a class.
The result of asynchronous task execution, the main thread is not available
FutureTask in Java
FutureTask can be used in scenarios where an execution result is retrieved asynchronously or a task is cancelled. By passing a Runnable or Callable task to FutureTask, calling its RUN method or putting it into a thread pool for execution, FutureTask can then get the result of the execution asynchronously externally via FutureTask’s GET method. Therefore, FutureTask is very suitable for time-consuming calculations. The main thread can retrieve the result after completing its task. In addition, FutureTask can ensure that even if the run method is called multiple times, it will only execute a Runnable or Callable task once, or cancel the execution of FutureTask with cancel, etc.
- With FutureTask and ExecutorService, you can submit computing tasks in a multithreaded manner, and the main thread continues to perform other tasks. When the main thread needs the results of the child threads, it asynchronously obtains the results of the child threads.
public class FutureTaskForMultiCompute {
public static void main(String[] args) {
FutureTaskForMultiCompute inst=new FutureTaskForMultiCompute();
// Create a task set
List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
// Create a thread pool
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
// Pass a Callable object to create a FutureTask object
FutureTask<Integer> ft = new FutureTask<Integer>(inst.new ComputeTask( ""+i));
taskList.add(ft);
// Commit tasks to the thread pool, or commit all tasks at once via exec. InvokeAll (taskList);
executor.submit(ft);
}
System.out.println("After all computations are committed, the main thread moves on to other tasks!");
// Start counting results of each thread
Integer totalResult = 0;
for (FutureTask<Integer> ft : taskList) {
try {
System.out.println("Child thread return value:+ ft.get());
//FutureTask's get method blocks automatically until the result is retrieved
totalResult = totalResult + ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch(ExecutionException e) { e.printStackTrace(); }}// Close the thread pool
executor.shutdown();
System.out.println("The total result of ----------- multitasking calculation is :" + totalResult);
}
private class ComputeTask implements Callable<Integer> {
private int result = 0;
private String taskName = "";
public ComputeTask( String taskName){
this.taskName = taskName;
System.out.println("Generate child thread computation task:"+taskName);
}
public String getTaskName(a){
return this.taskName;
}
@Override
public Integer call(a) throws Exception {
for (int i = 0; i < 5; i++) {
result += i;
}
// Sleep for 5 seconds and observe the behavior of the main thread. The expected result is that the main thread will continue to execute until the result of retrieving the FutureTask is to wait until it completes.
Thread.sleep(5000);
System.out.println("The child thread name:"+Thread.currentThread().getName() );
System.out.println("Child thread computation task:"+taskName+"Execution complete!");
returnresult; }}}Copy the code
Result output:
Generation sub-thread computing task: 0 Generation sub-thread computing task: 1 Generation sub-thread computing task: 2 Generation sub-thread computing task: 3 Generation sub-thread computing task: 4 Generation sub-thread computing task: 5 Generation sub-thread computing task: 6 Generation sub-thread computing task: 7 Generation sub-thread computing task: Generate child thread calculation tasks: 9 All calculation tasks are submitted, the main thread then do other things! The subthread name is pool-1-thread-3. The subthread name is pool-1-thread-3. The subthread name is pool-1-thread-1. The subthread name is pool-1-thread-1. The subthread name is pool-1-thread-5. The subthread name is pool-1-thread-5. Subthread name: pool-1-thread-2 Subthread Computing task: 1 The execution is complete! The name of the subthread is pool-1-thread-4. Calculation task: 3 The execution is complete. Child thread Returned value: 10 Child thread returned value: 10 Child thread returned value: 10 Child thread returned value: 10 Child thread name: pool-1-thread-2 Child thread Computing task: 8 The execution is complete! The name of the subthread is pool-1-thread-5. Computing tasks: 7 The execution is complete. The name of the subthread is pool-1-thread-4. Computing tasks: 9 The execution is complete. The subthread name is pool-1-thread-3. The subthread name is pool-1-thread-3. Name of the subthread: pool-1-thread-1 Subthread Computing task: 6 Complete! Child thread return value :10 Child thread return value :10 Child thread return value :10 child thread return value :10 child thread return value :10 ----------- The total result after multi-task calculation is :100Copy the code
As you can see, the number of threads that can run at one time is five. This means that when we start 10 tasks, only 5 tasks can be executed immediately, the other 5 tasks have to wait, and when one task is completed, the sixth task can be started, and so on
- FutureTask ensures that tasks are executed only once in high-concurrency environments. In many high-concurrency environments, we often only need certain tasks to be executed only once. This use scenario FutureTask feature fits the bill. For example, suppose there is a connection pool with a key. If the key exists, the object corresponding to the key is returned directly. When the key does not exist, the connection is created. For such applications, a common approach is to use a Map object to store the mapping between keys and connection pools. Typical code is as follows:
private Map<String, Connection> connectionPool = new HashMap<String, Connection>();
private ReentrantLock lock = new ReentrantLock();
public Connection getConnection(String key){
try{
lock.lock();
if(connectionPool.containsKey(key)){
return connectionPool.get(key);
}
else{
/ / create a Connection
Connection conn = createConnection();
connectionPool.put(key, conn);
returnconn; }}finally{ lock.unlock(); }}/ / create a Connection
private Connection createConnection(a){
return null;
}
Copy the code
In the example above, we lock to ensure thread-safety in a high-concurrency environment and ensure that the Connection is created only once, at the expense of performance. When using ConcurrentHash, locking is almost avoided and performance is greatly improved. However, in high concurrency cases, connections may be created more than once. This is where FutureTask comes into play. The code based on ConcurrentHashMap and FutureTask is as follows:
private ConcurrentHashMap<String,FutureTask<Connection>>connectionPool = new ConcurrentHashMap<String, FutureTask<Connection>>();
public Connection getConnection(String key) throws Exception{
FutureTask<Connection>connectionTask=connectionPool.get(key);
if(connectionTask! =null) {return connectionTask.get();
}
else{
Callable<Connection> callable = new Callable<Connection>(){
@Override
public Connection call(a) throws Exception {
// TODO Auto-generated method stub
returncreateConnection(); }}; FutureTask<Connection>newTask =new FutureTask<Connection>(callable);
connectionTask = connectionPool.putIfAbsent(key, newTask);
if(connectionTask==null){
connectionTask = newTask;
connectionTask.run();
}
returnconnectionTask.get(); }}/ / create a Connection
private Connection createConnection(a){
return null;
}
Copy the code
Java FutureTask asynchronous task operations provide convenience 1. Obtain the return value of an asynchronous task. 2. Listen to the completion of an asynchronous task. 3
AsyncTask in Android
AsyncTask source
public abstract class AsyncTask<Params.Progress.Result> {
private static final String LOG_TAG = AsyncTask ";" Private static final int CPU_COUNT = Runtime.getruntime (). AvailableProcessors (); private static final int CPU_COUNT = Runtime.getruntime (). Private static final int CORE_POOL_SIZE = math.max (2, math.min (cpu_count-1,4)); Private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; Private static final int KEEP_ALIVE_SECONDS = 30; private static final int KEEP_ALIVE_SECONDS = 30; Private static final ThreadFactory sThreadFactory = new ThreadFactory() {// An atomic integer Private Final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); }}; // Static blocking queue, used to store tasks to be executed, initial capacity: Private static final BlockingQueue
sPoolWorkQueue = new LinkedBlockingQueue
(128); Public static final Executor THREAD_POOL_EXECUTOR; public static final Executor THREAD_POOL_EXECUTOR; static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; Public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); Private static final int MESSAGE_POST_RESULT = 0x1; Private static final int MESSAGE_POST_PROGRESS = 0x2; private static final int MESSAGE_POST_PROGRESS = 0x2; Private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; private static volatile Executor sDefaultExecutor; // Static Handler. Asynctasks must be executed in the UI thread because the Handler uses the UI thread's Looper. Child threads do not have a Looper private static InternalHandler sHandler. private final WorkerRunnable
mWorker; private final FutureTask
mFuture; // Task Status is PENDING by default. Volatile private volatile Status mStatus = status.pending; Private final AtomicBoolean mCancelled = new AtomicBoolean(); // Whether the task has been executed Private Final AtomicBoolean mTaskInvoked = new AtomicBoolean(); // Serial task executor, Private static Class SerialExecutor implements Executor {// A linear bidirectional queue that stores all AsyncTask tasks final ArrayDeque
mTasks = new ArrayDeque
(); // Current task Runnable mActive; Public synchronized void execute(final Runnable r) {mtasks.offer (new Runnable() {public void run() { Try {// Execute task r.run(); } finally {// If there are tasks, the next task scheduleNext() is executed after the last task; }}}); If (mActive == null) {scheduleNext(); Protected synchronized void scheduleNext() {if ((mActive = mtasks.poll ())! = null) { THREAD_POOL_EXECUTOR.execute(mActive); }}} // The task status is waiting for execution, executing, Public enum Status {PENDING, RUNNING, FINISHED, Private static Handler getHandler() {synchronized (asynctask.class) {if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; }} public static void setDefaultExecutor(Executor exec) {sDefaultExecutor = exec; //AsyncTask constructor public AsyncTask() {mWorker = new WorkerRunnable
() { public Result call() throws Exception { //... //result = doInBackground(mParams); / /... return result; }}; mFuture = new FutureTask
(mWorker) { @Override protected void done() { //... }}; } private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (! wasTaskInvoked) { postResult(result); Private Result postResult(Result Result) {@suppressWarnings ("
,>
,>
unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult
(this, result)); message.sendToTarget(); return result; } public final Status getStatus() {return mStatus; } // The abstract class executes @workerThread protected abstract Result doInBackground(Params... params); @mainThread protected void onPreExecute() {} @mainThread protected void onPostExecute(Result) Result) {} @mainThread protected void onProgressUpdate(Progress... Values) {} //Cancel is called and doInBackground is completed, onCancelled is called, indicating that the task is cancelled. OnPostExecute will not be called @mainThread protected void onCancelled(Result Result) {onCancelled(); } @MainThread protected void onCancelled() { } public final boolean isCancelled() { return mCancelled.get(); } // Cancel the task being executed public final Boolean Cancel (Boolean mayInterruptIfRunning) {mCancelled. Set (true); return mFuture.cancel(mayInterruptIfRunning); } public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } @mainThread public Final AsyncTask
execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @mainThread public final AsyncTask
executeOnExecutor(Executor exec, Params... params) { if (mStatus ! = Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("
,>
,>
Cannot execute task:"+" the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:"+" the task has already been executed "+"(a task can be executed only once)"); } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; @workerThread protected final void publishProgress(Progress... values) { if (! isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResultunchecked","RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult
result = (AsyncTaskResult
) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } } private static abstract class WorkerRunnable
implements Callable
{ Params[] mParams; } @SuppressWarnings({"
,>RawUseOfParameterizedType"}) private static class AsyncTaskResult { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; }}}Copy the code
Key source code:
private static Handler getHandler(a) {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
returnsHandler; }}/ * *@hide* /
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */
public AsyncTask(a) {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call(a) throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
returnresult; }}; mFuture =new FutureTask<Result>(mWorker) {
@Override
protected void done(a) {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null); }}}; }Copy the code
AsyncTask encapsulates Thead, FutureTask, and Handler internally.
Problem 1: Insufficient thread pool capacity throws an exception
public class AsyncTaskTest {
public static void main(String[] args) {
int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // The number of available cpus
int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1.4));// Number of core threads
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//9 Maximum number of threads
int KEEP_ALIVE_SECONDS = 1;// Idle collection time
final BlockingDeque<Runnable> sPoolWorkQueue = new LinkedBlockingDeque<Runnable>(128);// Asynchronous task queue
// sThreadFactory: thread factory
final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
String name = "Thread #" + mCount.getAndIncrement();
System.out.println(name);
return newThread(r, name); }};/ / thread pool
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
// Perform asynchronous tasks
for(int i =0; i <200; i++){// new AsyncTask().execute();
threadPoolExecutor.execute(newMyTask()); }}static class MyTask implements Runnable{
@Override
public void run(a) {
while(true) {try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Copy the code
Result output:
Thread #1 Thread #2 Thread #3 Thread #2 Thread #1 Thread #3 Thread #4 Thread #5 Thread #6 Thread #4 Thread #5 Thread #7 Thread #6 Thread #8 Thread #7 Thread #9 Thread #8 Thread #9 Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.haocai.app.multithread.test.AsyncTaskTest$MyTask@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369) at com.haocai.app.multithread.test.AsyncTaskTest.main(AsyncTaskTest.java:45) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Thread #4 Thread #1 ...... Omit...Copy the code
We found that there will be abnormal Java. Util. Concurrent. RejectedExecutionException
If the number in the current thread pool is less than corePoolSize, the task is created and added. If the number of threads in the current thread pool is equal to corePoolSize and the buffer queue workQueue is not full, the task is put into the buffer queue and wait for the task to be scheduled to execute. If the number of threads in the current thread pool is greater than corePoolSize, the buffer queue workQueue is full, and the number of threads in the thread pool is less than maximumPoolSize, a new submitted task creates a new thread to execute the task. If the number of threads in the current thread pool is greater than corePoolSize, the buffer queue workQueue is full, and the number of threads in the pool is equal to maximumPoolSize, the new submitted task is processed by the Handler. When the number of threads in the thread pool is larger than corePoolSize and the idle time of redundant threads exceeds keepAliveTime, the thread is closed.
Solution: The thread pool is expanded
// Custom thread pool
Executor executor = Executors.newScheduledThreadPool(25);// Specifies the number of core thread pools
Copy the code
Problem two: Thread blocking
AsyncTask maintains two thread pools, THREAD_POOL_EXECUTOR and SERIAL_EXECUTOR, of which SERIAL_EXECUTOR is the default thread pool
Look at the source code for API22 SerialExecutor
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run(a) {
try {
r.run();
} finally{ scheduleNext(); }}});if (mActive == null) { scheduleNext(); }}protected synchronized void scheduleNext(a) {
if((mActive = mTasks.poll()) ! =null) { THREAD_POOL_EXECUTOR.execute(mActive); }}}Copy the code
It can be seen from the above source code that scheduleNext will be called to add tasks to the thread pool after each task is executed. Therefore, even though the thread pool is parallel, I add tasks in serial, so the AsyncTask in API22 is serial, so that no matter how many threads there are in the thread pool, it is useless. There’s only one mission in there at a time anyway.
And since SERIAL_EXECUTOR is declared static, asynctasks in the same process share the thread pool, which means that subsequent threads in the same process are suspended until the first thread terminates.
Solution:
So, using AsyncTask mission, please use AsyncTask. ExecuteOnExecutor (THREAD_POOL_EXECUTOR) to make your task to run in parallel thread pool, avoid and thread block in front of the situation. Of course, if you have a large number of CPU cores and the parallelism of 2 to 4 threads is not sufficient, you can also create a custom thread pool to perform AsyncTask, but in this case, you need to maintain the pool initialization, release, etc.
new AsyncTask<Void, Void, Void>(){
@Override
protected Void doInBackground(Void... params) {
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Copy the code
Note: You can use executeOnExecutor with two arguments if you are sure that you are synchronous, or if you are not accessing shared resources in different AsyncTasks and need asyncTasks to be able to process tasks in parallel
The Android AsyncTask version is faulty
When AsyncTask was first introduced, the execute method was executed serally, with only the SERIAL_EXECUTOR thread pool in the class definition.
In version 1.6, the parallel thread pool THREAD_POOL_EXECUTOR was switched to,
In version 3.0, this is the case ———— two thread pools are defined, but serial pools are used by default.
Problem three: Memory leakage
public class MainActivity extends AppCompatActivity {
private MyTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Use the default thread pool
task = new MyTask();
task.execute();
}
class MyTask extends AsyncTask<Void.Integer.Void> {
int i;
@Override
protected Void doInBackground(Void... params) {
Log.d("main", String.valueOf(i++));
SystemClock.sleep(1000);
return null; }}}Copy the code
After Activity Finish (), MyTask is observed to be still executing, causing a memory leak
Solution:
public class MainActivity extends AppCompatActivity {
private MyTask task;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Use the default thread pool
task= new MyTask();
task.execute();
}
@Override
protected void onDestroy(a) {
super.onDestroy();
task.cancel(true);
}
class MyTask extends AsyncTask<Void.Integer.Void> {
int i;
@Override
protected Void doInBackground(Void... params) {
while(! isCancelled()){ Log.d("main", String.valueOf(i++));
SystemClock.sleep(1000);
}
return null; }}}Copy the code