前言:在日常Android开发过程中,“线程”用处十分广泛,本篇文章将从线程的常用属性和方法、通讯方式、线程安全等角度进行总结,并通过有效的示例demo,带大家一起解读Android多线程的魅力。
首先看一下本篇博客的内容概要:
一、线程的属性和常用方法:
1. 线程的非静态方法方法 start()、run()、join()、interrupt、isInterruped() 和 静态方法 Thread.currentThread()、Thread.sleep、Thread.yield()、Thread.interrupted
/*示例demo: 主要介绍join配合其他方法的使用。如果线程 A 调用了线程 B 的 join() 方法,那线程 A 会进入等待状态,直到线程 B 运行结束。*/
import android.support.annotation.NonNull;
public class ThreadDemo {
private static ThreadA mThreadA;
private static ThreadB mThreadB;
public static void main(String[] args) {
mThreadA = new ThreadA("Thread-A");
mThreadB = new ThreadB("Thread-B");
mThreadA.start();
mThreadB.start();
}
public static class ThreadA extends Thread {
public ThreadA(@NonNull String name) {
super(name);
}
@Override
public void run() {
try {
System.out.println("A线程开始-" + Thread.currentThread().getName());
mThreadB.join();
System.out.println("A线程结束-" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class ThreadB extends Thread {
public ThreadB(@NonNull String name) {
super(name);
}
@Override
public void run() {
try {
long start = System.currentTimeMillis();
System.out.println("B线程开始,开始时间为" + start);
Thread.sleep(3000);
long end = System.currentTimeMillis();
System.out.println("B线程结束,结束时间为" + end + ",共耗时" + (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
------------------------代码输出结果------------------------------
A线程开始-Thread-A
B线程开始,开始时间为1587655657577
B线程结束,结束时间为1587655660578,共耗时3001
A线程结束-Thread-A
2. object的方法:wait()、notify()、notifyAll()
/*示例demo: 主要介绍wait配合notify的相关使用。*/
public class ThreadDemo2 {
public static void main(String[] args) {
Object lock = new Object();
WaitThread t1 = new WaitThread(lock);
t1.start();
NotifyThread t2 = new NotifyThread(lock);
t2.start();
}
public static class NotifyThread extends Thread {
private final Object lock;
public NotifyThread(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
// 做一些业务逻辑相关的事。。。。
System.out.println("开始 notify time= " + System.currentTimeMillis());
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
// 业务逻辑完成了...
lock.notify();
System.out.println("结束 notify time= " + System.currentTimeMillis());
}
}
}
public static class WaitThread extends Thread {
private final Object lock;
public WaitThread(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
long start = System.currentTimeMillis();
System.out.println("开始 wait time= " + start);
lock.wait();
long end = System.currentTimeMillis();
System.out.println("结束 wait time= " + end + ",共耗时" + (end - start));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
------------------------代码输出结果------------------------------
开始 wait time= 1587656109916
开始 notify time= 1587656109916
结束 notify time= 1587656112917
结束 wait time= 1587656112917,共耗时3001
3. 线程的状态:new(新建)、runnable(可运行)、blocked(阻塞)、sleep/join/wait/yield(等待/限时等待)、terminated(终止)
new : 线程被创建
runnable : 线程处于可运行或者正在运行状态,
blocked : 线程被阻塞,等待获取对象锁
sleep : 休眠状态,过程不释放对象锁
join : 在B线程中执行A.join,则B进入等待状态,直到A执行完毕
yield : 降低线程优先级,如果其他线程需要则让给其他线程,但此方法不一定会使线程处于暂停状态
4. 线程的类别:setDaemon(守护线程,在线程启动前设置)、用户线程。
/*示例demo:守护线程通常是为用户线程提供某种服务的,当用户线程结束后,守护线程也会停止。比如GC线程就属于守护线程*/
public class ThreadDaemon {
public static void main(String[] args) {
//新建子线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
//子线程每隔1s打印一次
try {
Thread.sleep(1000);
System.out.println("本地i的值为:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.setDaemon(true); //如果注释掉这一句则打印1到5的结果
thread.start();
//用户线程(主线程在2s后结束)
try {
Thread.sleep(2001);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
------------------------代码输出结果------------------------------
本地i的值为:1
本地i的值为:2
5. 线程的优先级:两种方式设置 1、通过Thread.setPriority(Thread.NORM_PRIORITY); 2、Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO):
int THREAD_PRIORITY_AUDIO = -16 //标准音乐播放使用的线程优先级
int THREAD_PRIORITY_BACKGROUND = 10 //标准后台程序
int THREAD_PRIORITY_DEFAULT = 0// 默认应用的优先级
int THREAD_PRIORITY_DISPLAY = -4 //标准显示系统优先级,主要是改善UI的刷新
int THREAD_PRIORITY_FOREGROUND = -2 //标准前台线程优先级
int THREAD_PRIORITY_LESS_FAVORABLE = 1 //低于favorable
int THREAD_PRIORITY_LOWEST = 19 //有效的线程最低的优先级
int THREAD_PRIORITY_MORE_FAVORABLE = -1//高于favorable
int THREAD_PRIORITY_URGENT_AUDIO = -19 //标准较重要音频播放优先级
int THREAD_PRIORITY_URGENT_DISPLAY = -8 //标准较重要显示优先级,对于输入事件同样适用.
二、线程间的通讯方式有哪些:
1. Handler + Thread 与 HandlerThread 的方式
/*示例demo:简单采用HandlerThread的方式,进行线程间的通信(类似于一个轻量级的线程池),提供了主线程向子线程的通信*/
public class MainActivity2 extends AppCompatActivity {
private static TextView mTextView;
private static Button mButton;
private HandlerThread mHandlerThread;
private Handler mWorkHandler;
private int msgObg;
@SuppressLint("HandlerLeak")
private Handler mUIHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("主线程打印出了" + msg.obj);
mTextView.setText("改变文字为:" + msg.obj);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
mButton = findViewById(R.id.button);
mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
mWorkHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
System.out.println("子线程打印出了" + msg.obj);
//方式一:通过消息给mUIHandler
/* Message message = new Message();
message.obj = msg.obj;
mUIHandler.sendMessage(message);*/
//方式二:直接post执行,,但是要注意不能直接访问msg.obj
msgObg = (int) msg.obj;
mUIHandler.post(new Runnable() {
@Override
public void run() {
System.out.println("主线程打印出了" + msgObg);
mTextView.setText("改变文字为:" + msgObg);
}
});
break;
}
}
};
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i = 0; i < 5; i++) {
Message message = new Message();
message.what = 1;
message.obj = i;
mWorkHandler.sendMessageDelayed(message, 1000 * i);
}
}
});
}
}
------------------------代码输出结果------------------------------
I/System.out: 子线程打印出了0
I/System.out: 主线程打印出了0
I/System.out: 子线程打印出了1
I/System.out: 主线程打印出了1
I/System.out: 子线程打印出了2
I/System.out: 主线程打印出了2
I/System.out: 子线程打印出了3
I/System.out: 主线程打印出了3
I/System.out: 子线程打印出了4
I/System.out: 主线程打印出了4
2. View.post (Runnable) 与 Handler.postDelay (Runnable , long)
//说明:该方法的使用时机是当前view在attachdToWindow之后进行调用的。其中post的调用时机分为两种:
// 当 View 还没 attachedToWindow 时,会先将这些 Runnable 操作缓存下来,等attachedToWindow之后在主线程执行。
// 否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行。
// view.post方法替代了创建新的handler
public class MainActivity3 extends AppCompatActivity {
private static TextView mTextView;
private static Button mButton;
private LinearLayout mRootView;
@SuppressLint("HandlerLeak")
private Handler mUIHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
mButton = findViewById(R.id.button);
mRootView = findViewById(R.id.rootLayout);
final View view = new View(this);
//注意该方法是在主线程中执行的,替代了创建新的handler
view.postDelayed(new Runnable() {
@Override
public void run() {
mTextView.setText("使用view.post改变UI");
System.out.println("MainActivity3这是使用view.post的方式改变UI");
}
}, 3000);
mUIHandler.postDelayed(new Runnable() {
@Override
public void run() {
mTextView.setText("使用handler改变UI");
System.out.println("MainActivity3这是使用handler的方式改变UI");
}
}, 6000);
}
------------------------代码输出结果------------------------------
05-05 23:11:46.283 4038-4038/com.zziafyc.mytestapplication I/System.out: MainActivity3这是使用view.post的方式改变UI
05-05 23:11:49.226 4038-4038/com.zziafyc.mytestapplication I/System.out: MainActivity3这是使用handler的方式改变UI
3. AsyncTask(源码解读?)
//示例demo,通过AsyncTask,完成访问网络图片的耗时操作
public class MainActivity9 extends AppCompatActivity {
private Button mButton;
private ImageView mImageView;
private static final String url = "https://profile.csdnimg.cn/F/3/4/3_u013769274";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.button5);
mImageView = findViewById(R.id.image);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyAsyncTask().execute(url);
}
});
}
class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//初始化工作
System.out.println("开始相关初始化工作!");
}
@Override
protected Bitmap doInBackground(String... strings) {
String url = strings[0];
Bitmap bitmap = null;
URLConnection connection;
InputStream inputStream;
try {
connection = new URL(url).openConnection();
inputStream = connection.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
bitmap = BitmapFactory.decodeStream(bufferedInputStream);
inputStream.close();
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mImageView.setImageBitmap(bitmap);
}
}
}
------------------------代码输出结果------------------------------
4. LoaderManager(源码解读?)
1. Loader机制一般用于在单独的线程中读取数据,不会阻塞UI,特别是用于加载 ContentProvider 中的内容,
同时能够监听数据的更新,比起 Handler + Thread 或者 AsyncTask 的实现方式,Loader 机制能让代码更加的简洁易懂,
而且是 Android 3.0 之后最推荐的加载方式,LoaderManager是loader的管理器,通常一个Activity或者Fragment只能有一个
LoadManager。LoaderManager管理Loader的初始化,重启和销毁操作。
3. Loader机制的使用场景 有:
一:展现某个Android手机有多少应用程序
二:加载手机中的图片、视频、音乐资源
三:访问用户联系人
4. 示例demo
public class MainActivity10 extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private Button mButton;
private ListView mListView;
// 使用SimpleCursorAdapter来填充数据
private SimpleCursorAdapter mAdapter;
// 使用CursorLoader来获取数据
private CursorLoader loader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mButton = findViewById(R.id.button);
mListView = findViewById(R.id.listView);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//申请权限
requestPermission(MainActivity10.this);
}
});
}
public boolean requestPermission(final Context context) {
int currentAPIVersion = Build.VERSION.SDK_INT;
if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
(Activity) context,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
showDialog("External storage", context, Manifest.permission.READ_EXTERNAL_STORAGE);
} else {
ActivityCompat
.requestPermissions(
(Activity) context,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
1);
}
return false;
} else {
return true;
}
} else {
return true;
}
}
public void showDialog(final String msg, final Context context,
final String permission) {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
alertBuilder.setCancelable(true);
alertBuilder.setTitle("Permission necessary");
alertBuilder.setMessage(msg + " permission is necessary");
alertBuilder.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions((Activity) context,
new String[]{permission}, 1);
}
});
AlertDialog alert = alertBuilder.create();
alert.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initMusic();
} else {
Toast.makeText(MainActivity10.this, "GET_ACCOUNTS Denied",
Toast.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions,
grantResults);
}
}
private void initMusic() {
// 这里创建Adapter时 注意不传递数据
mAdapter = new SimpleCursorAdapter(MainActivity10.this, R.layout.item_music,
null, new String[]{MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST}, new int[]{
R.id.music_name, R.id.music_singer}, 0);
mListView.setAdapter(mAdapter);
// 通过异步的方式加载数据
LoaderManager manager = getLoaderManager();
// 第一个参数为id 第二个位Bundle数据 第三个为LoaderCallbacks
manager.initLoader(0, null, MainActivity10.this);
}
// 首先检查指定的id是否存在,如果不存在才会触发该方法,通过该方法才能创建一个loader。
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// 查询音乐数据库 获取音乐数据 并排序
loader = new CursorLoader(this,
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
return loader;
}
// 完成对Ui控件的更新,如果不再使用,将自动释放loader的数据,不需要使用close();
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
5. service 和 IntentService
1. 通常service的做法是在onStartCommand()方法中创建一个新thread来执行耗时操作,任务执行完成后需要在run方法中手动调用stopself方法停止服务。
/**
* 调用方Activity
*/
public class MainActivity5 extends AppCompatActivity {
private Button bindServiceButton;
private Button unbindServiceButton;
MyService.MyBinder myBinder;
public static final String TAG = "MainActivity5";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindServiceButton = findViewById(R.id.button);
unbindServiceButton = findViewById(R.id.button2);
bindServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动服务方式一:
Intent bindIntent = new Intent(MainActivity5.this, MyService.class);
MainActivity5.this.bindService(bindIntent, connection, BIND_AUTO_CREATE);
//启动服务方式二:
/* Intent startIntent = new Intent(MainActivity5.this, MyService.class);
startService(startIntent);*/
}
});
unbindServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//方式一:解绑服务,此时服务没有停止,可以在unbind的时候使服务停止。如果服务里面的耗时线程没有执行完毕,则线程依然不会结束。
MainActivity5.this.unbindService(connection);
//方式二:停止服务,此时如果服务里面的耗时线程没有执行完毕,则线程依然不会结束。
/* Intent bindIntent = new Intent(MainActivity5.this, MyService.class);
stopService(bindIntent);*/
}
});
/**
* 自定义service
*/
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate() executed");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//只有通过startService的方式,才会调用。
Log.d(TAG, "onStartCommand() executed");
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
System.out.println("启动服务,开始打印喽:" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行完成后,关闭服务。
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() executed");
}
@Override
public boolean onUnbind(Intent intent) {
//执行解绑的时候,关闭服务
Log.d(TAG, "onUnbind()");
stopSelf();
return super.onUnbind(intent);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class MyBinder extends Binder {
//该方法为了方便启动service的context调用,一般是绑定了之后使用
public void startDownload() {
Log.d("TAG", "startDownload() executed");
// 执行具体的下载任务
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
System.out.println("context调用服务binder,开始打印喽:" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
}
}
------------------------代码输出结果------------------------------
05-06 23:15:32.203 5129-5129/com.zziafyc.mytestapplication D/MyService: onCreate() executed
05-06 23:15:32.206 5129-5129/com.zziafyc.mytestapplication D/MainActivity5: onServiceConnected
05-06 23:15:32.206 5129-5129/com.zziafyc.mytestapplication D/TAG: startDownload() executed
05-06 23:15:32.218 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:0
05-06 23:15:33.221 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:1
05-06 23:15:34.221 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:2
05-06 23:15:35.221 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:3
05-06 23:15:36.222 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:4
05-06 23:15:37.222 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:5
05-06 23:15:38.223 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:6
05-06 23:15:39.223 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:7
05-06 23:15:40.224 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:8
05-06 23:15:41.224 5129-5157/com.zziafyc.mytestapplication I/System.out: context调用服务binder,开始打印喽:9
05-06 23:15:59.263 5129-5129/com.zziafyc.mytestapplication D/MyService: onUnbind()
05-06 23:15:59.268 5129-5129/com.zziafyc.mytestapplication D/MyService: onDestroy() executed
2. 然而IntentService方法可以很方便地解决自己开启线程和手动停止服务的问题。
/**
* 调用方Activity
*/
public class MainActivity6 extends AppCompatActivity {
private Button intentServiceButton;
public static final String TAG = "MainActivity6";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentServiceButton = findViewById(R.id.button3);
intentServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "主线程的id= " + Thread.currentThread().getId());
Intent intentService = new Intent(MainActivity6.this, MyIntentService.class);
startService(intentService);
}
});
}
}
/**
* 自定义intentService
*/
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); //调用父类的有参构造函数
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//打印当前线程的id
Log.d("MyIntentService", "当前线程的id=" + Thread.currentThread().getId());
for (int i = 0; i < 10; i++) {
try {
System.out.println("执行onHandleIntent,开始打印喽:" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy");
}
}
------------------------代码输出结果------------------------------
05-06 23:11:40.454 5020-5020/com.zziafyc.mytestapplication D/MainActivity6: 主线程的id= 1
05-06 23:11:40.489 5020-5043/com.zziafyc.mytestapplication D/MyIntentService: 当前线程的id=190
05-06 23:11:40.489 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:0
05-06 23:11:41.490 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:1
05-06 23:11:42.490 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:2
05-06 23:11:43.491 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:3
05-06 23:11:44.491 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:4
05-06 23:11:45.492 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:5
05-06 23:11:46.493 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:6
05-06 23:11:47.494 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:7
05-06 23:11:48.494 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:8
05-06 23:11:49.495 5020-5043/com.zziafyc.mytestapplication I/System.out: 执行onHandleIntent,开始打印喽:9
05-06 23:11:50.498 5020-5020/com.zziafyc.mytestapplication D/MyIntentService: onDestroy
6. ThreadPoolExecutor (线程池)
1. Android为我们提供了四种线程池,(newFixThreadPool、newSingleThreadPool 、newCachedThreadPool、newScheduledThreadPool,因为各有局限性,阿里巴巴开发手册建议我们采用ThreadPoolExecutor 的方式创建线程池,有助于大家明确线程池的运行规则。
2. ThreadPoolExecutor 有多个重载方法,但最终都调用了这个构造方法。
/*
corePoolSize: 线程池中核心线程的数量。
maximumPoolSize:线程池中最大线程数量。
keepAliveTime:非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长。
unit:keepAliveTime这个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等。
workQueue:线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
threadFactory:为线程池提供创建新线程的功能,这个我们一般使用默认即可。
RejectedExecutionHandler: 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
3. /*
ThreadPoolExecutor 执行任务时大致遵循如下流程:
1.如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。
2.如果线程池中的线程数已经达到核心线程数,且任务队列workQueue未满,则将新线程放入workQueue中等待执行。
3.如果线程池中的线程数已经达到核心线程数但未超过线程池规定最大值(corePoolSize + workQueue),且workQueue已满,则开启一个非核心线程来执行任务。
4.如果线程池中的线程数已经超过线程池规定最大值,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。
5.下面例子中设置的任务队列长度为50,任务只有30个,任务队列未满,只走到第二个流程,不会开启额外的5-3=2个非核心线程,
如果将任务队列设为25,则前三个任务被核心线程执行,剩下的30-3=27个任务进入队列会满超出2个,此时会开启2个非核心线程来执行剩下的两个任务,这是刚好达到线程池的最大线程数,假如还有任务,将会拒绝执行,抛出RejectedExecutionException异常。
*/
public class MainActivity7 extends AppCompatActivity {
private Button threadPoolButton;
public static final String TAG = "MainActivity7";
ThreadPoolExecutor threadPoolExecutor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
threadPoolButton = findViewById(R.id.button4);
threadPoolButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("点击了,开始执行");
threadPoolExecutor = new ThreadPoolExecutor(3, 5, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(50));
//for循环创建线程时,线程很快创建,30个线程几乎是同时创建的,但是加入线程池管理后,每3个核心线程会同时执行,执行持续3s之后,其他3个可执行的线程也开始成为核心线程并开始执行,
// 依次下去,最终的结果则是每3s打印3次日志,会打印10组。
for (int i = 0; i < 30; i++) {
threadPoolExecutor.execute(new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 哈哈");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}));
}
}
});
}
}
------------------------代码输出结果------------------------------
05-09 00:55:13.946 4504-4504/com.zziafyc.mytestapplication I/System.out: 点击了,开始执行
05-09 00:55:13.954 4504-4534/com.zziafyc.mytestapplication I/System.out: pool-1-thread-3 哈哈
05-09 00:55:13.983 4504-4532/com.zziafyc.mytestapplication I/System.out: pool-1-thread-1 哈哈
05-09 00:55:13.984 4504-4533/com.zziafyc.mytestapplication I/System.out: pool-1-thread-2 哈哈
05-09 00:55:16.955 4504-4534/com.zziafyc.mytestapplication I/System.out: pool-1-thread-3 哈哈
05-09 00:55:16.983 4504-4532/com.zziafyc.mytestapplication I/System.out: pool-1-thread-1 哈哈
05-09 00:55:16.984 4504-4533/com.zziafyc.mytestapplication I/System.out: pool-1-thread-2 哈哈
05-09 00:55:19.955 4504-4534/com.zziafyc.mytestapplication I/System.out: pool-1-thread-3 哈哈
05-09 00:55:19.983 4504-4532/com.zziafyc.mytestapplication I/System.out: pool-1-thread-1 哈哈
05-09 00:55:19.984 4504-4533/com.zziafyc.mytestapplication I/System.out: pool-1-thread-2 哈哈
------------------------后面还有7组,每组打印3条记录------------------------
7. EventBUs实现异步线程(源码解读?)
1. 注册事件(成对出现)
EventBus.getDefault().register(this);
2. 取消注册事件
EventBus.getDefault().unregister(this);
3. 发送事件
EventBus.getDefault().post(messageEvent);
EventBus.getDefault().postSticky(messageEvent);
4. 处理事件
@Subscribe(threadMode = ThreadMode.MAIN, priority = 1, sticky = true)
public void notifyMessage(MessageEvent messageEvent) {
}
8. Rxjava实现异步线程(源码解读?)
//示例demo
ApiClient.getApiService().getAllUser()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<User>>() {
@Override
public void call(List<User> users) {
String content = users.get(0).getUserName() + "," + users.get(0).getPassword();
Log.e(TAG, "onNext: " + content);
contextTv.setText("内容为:" + content);
}
});
9. kotlin中协程实现异步线程(源码解读?)
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
private var button: Button? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.button)
button?.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
//通过调用await方法阻塞外部协程,所有代码都是按顺序执行的。这样适用于多个同级IO操作的情况
GlobalScope.launch(Dispatchers.Unconfined) {
var token = GlobalScope.async(Dispatchers.Unconfined) {
return@async getToken()
}.await()
var response = GlobalScope.async(Dispatchers.Unconfined) {
return@async getResponse(token)
}.await()
setText(response)
}
}
})
}
suspend fun getToken(): String {
delay(2000)
Log.d(TAG, "调用getToken方法,协程开始执行,当前时间: ${System.currentTimeMillis()}")
return "ask"
}
suspend fun getResponse(token: String): String {
delay(3000)
Log.d(TAG, "调用getToken方法,协程开始执行,当前时间: ${System.currentTimeMillis()}")
return "response"
}
fun setText(response: String) {
Log.d(TAG, "调用setText,协程开始执行,当前时间 ${System.currentTimeMillis()}")
}
}
------------------------代码输出结果------------------------------
05-11 00:31:44.320 3611-3642/com.zziafyc.mykotlinproject D/MainActivity: 调用getToken方法,协程开始执行,当前时间: 1589128304320
05-11 00:31:47.323 3611-3642/com.zziafyc.mykotlinproject D/MainActivity: 调用getToken方法,协程开始执行,当前时间: 1589128307323
05-11 00:31:47.323 3611-3642/com.zziafyc.mykotlinproject D/MainActivity: 调用setText,协程开始执行,当前时间 1589128307323