Java多线程
来源:互联网 发布:中国私营企业调查数据 编辑:程序博客网 时间:2024/05/03 12:47
今天研究了一下多线程,所谓多线程也就是为了解决并发的操作,常用的方法有一下三种:
1.继承Thread类或是实现Runable接口:
/** * Created by lan.zheng on 2016/9/12. */public class testActivity extends Activity{ Thread thread1; Thread thread2; Thread thread3; Thread thread4; boolean flag = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Goods g =new Goods(); flag =true; thread1 = new Thread(new Runnable(){ public void run(){ while(flag){ g.produce("商品"); } } },"生产者一号"); thread2 = new Thread(new Runnable(){ public void run(){ while(flag){ g.produce("商品"); } } },"生产者二号"); thread3 = new Thread(new Runnable(){ public void run(){ while(flag){ g.consume(); } } },"消费者一号"); thread4 = new Thread(new Runnable(){ public void run(){ while(flag){ g.consume(); } } },"消费者二号"); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } class Goods{ private boolean flags =false; private String name; private int num = 20; public synchronized void produce(String name){ while(flags) try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.name =name+"编号:"+num++; Log.d("test11",Thread.currentThread().getName()+"生产了...."+this.name); flags =true; notifyAll(); } public synchronized void consume(){ while(!flags) try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.d("test11",Thread.currentThread().getName()+"消费了******"+name); flags =false; notifyAll(); } } @Override protected void onDestroy() { super.onDestroy(); flag = false; thread1.interrupt(); thread2.interrupt(); thread3.interrupt(); thread4.interrupt(); }}这是多线程最简单的方法,不过要注意结束线程的问题:
关于线程的结束有以下几点:
(1).不要手动调用stop方法强行终止一个线程,这种方式不安全。这些方法Android本身都是不推荐使用的,通过这种方式结束线程是不安全的。
(2).线程里run函数短,执行完后线程会自行销毁,不用手动去终止。
(3).手动停止,通过在run里设置标志先停止运行,再调用Thread.interrupt();注意,在run没有停止时调用.interrupt()没有效果。
2.AsyncTask:
class AsyncTaskThread extends AsyncTask<String, Integer, Bitmap> { @Override protected Bitmap doInBackground(String... params) { } protected void onProgressUpdate(Integer... progress) { } protected void onPostExecute(Bitmap result) { } protected void onPreExecute() { } protected void onCancelled() { } }
AsyncTask内的各个方法调用顺序:
|- 首先,用户调用execute方法,启动AsyncTask 。然后在execute方法中:
|- 首先调用onPreExecute方法,执行初始化操作。
|- 然后从线程池中取出若干个空闲的线程,并使用该线程调用doInBackground方法,执行耗时的操作,如文件下载等。
|- 提示:调用execute方法时设置的参数会被直接传递给doInBackground方法。
|- 当doInBackground方法执行完毕后,onPostExecute方法将被调用。onPostExecute方法的参数就是doInBackground方法的返回值。
|- 若doInBackground方法中途被终止,则同样会调用onPostExecute方法,但是方法的参数却为null 。
|- 若想更新UI控件,则可以在doInBackground方法中调用publishProgress方法向主线程中的Handler发送消息,Handler接到消息后会转调用onProgressUpdate方法来更新UI。
|- 提示:调用publishProgress方法时设置的参数将被传递给onProgressUpdate方法。
3.线程池,写之前先和第一种方法比较一下:
new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
可以参考这两篇文章:http://www.trinea.cn/android/java-android-thread-pool/
http://www.eoeandroid.com/thread-210082-1-1.html?_dsign=1b202888
最后给个自己写的例子,不过该例子还是会有线程阻塞的现象,一定要等上一个返回结果了才能对下一个操作,原因是:ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
/** * Created by lan.zheng on 2016/9/12. */public class testActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } ListView listView; ExecutorService pool; List<String> test; TestAdapter testAdapter; private void initView(){ int taskSize = 5; // 创建一个线程池,大小为5 pool = Executors.newFixedThreadPool(taskSize); listView = (ListView) findViewById(R.id.lv_test); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { testAdapter.notifyClickChange(position); Log.d("test11",test.get(position).toString()); Callable c = new MyCallable(test.get(position).toString()); final Future f = pool.submit(c); try { f.get().toString(); testAdapter.notifyTextChange(position,f.get().toString()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }); test = new ArrayList<>(); test.add("1 task"); test.add("2 task"); test.add("3 task"); test.add("4 task"); test.add("5 task"); test.add("6 task"); test.add("7 task"); test.add("8 task"); test.add("9 task"); testAdapter = new TestAdapter(this,test); listView.setAdapter(testAdapter); } public class TestAdapter extends BaseAdapter { private List<String> mlistAppInfo = new ArrayList<>(); private List<String> list; LayoutInflater infater = null; String string = ""; public TestAdapter(Context context, List<String> apps) { infater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mlistAppInfo.addAll(apps); list = new ArrayList<>(); } public void notifyClickChange(int position){ for(int i = 0;i<list.size();i++){ if(list.get(i).equals(""+position)){ return; } } list.add(""+position); string = (position+1) + " task 开始 --> "; mlistAppInfo.set(position,string); notifyDataSetChanged(); } public void notifyTextChange(int position,String text){ mlistAppInfo.set(position,string+text); notifyDataSetChanged(); } @Override public int getCount() { // TODO Auto-generated method stub System.out.println("size" + mlistAppInfo.size()); return mlistAppInfo.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mlistAppInfo.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertview, ViewGroup arg2) { System.out.println("getView at " + position); View view = null; ViewHolder holder = null; if (convertview == null || convertview.getTag() == null) { view = infater.inflate(R.layout.list_item, null); holder = new ViewHolder(view); view.setTag(holder); } else { view = convertview; holder = (ViewHolder) convertview.getTag(); } for(int i = 0;i <list.size();i++){ if(list.get(i).equals(""+position)){ holder.linearLayout.setOnClickListener(null); holder.linearLayout.setBackgroundColor(getResources().getColor(R.color.test_color)); } } holder.tvAppLabel.setText(mlistAppInfo.get(position)); return view; } class ViewHolder { TextView tvAppLabel; LinearLayout linearLayout; public ViewHolder(View view) { this.tvAppLabel = (TextView) view.findViewById(R.id.tvAppLabel11); this.linearLayout = (LinearLayout)view.findViewById(R.id.layout); } } }}activity_main.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:background="#c4fce6"> <ListView android:id="@+id/lv_test" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollIndicators="none" android:divider="@null"> </ListView></RelativeLayout>list_item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="50dip" android:id="@+id/layout"> <RelativeLayout android:layout_width="fill_parent" android:layout_marginLeft="10dip" android:layout_height="40dip"> <TextView android:id="@+id/tvAppLabel11" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=""> </TextView> </RelativeLayout></LinearLayout>
/** * Created by lan.zheng on 2016/9/13. */public class MyCallable implements Callable<Object> { private String taskNum; MyCallable(String taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { return test(); } private String test(){ for(int i = 0;i<100000000;i++){ //这里用于模拟操作用时 } return taskNum+" 结束"; }}运行结果如下,点击一条将会完成一个任务:
如果不想要被阻塞,那我们不要用Future,使用runnable,然后用Handler去更新界面,改成如下:
Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { testAdapter.notifyTextChange(msg.what,msg.obj.toString()); } };
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { testAdapter.notifyClickChange(position); //点击之后不能再次点击设置 Log.d("test11",test.get(position).toString()); Runnable runnable1 = new Runnable() { //需要进行的操作 @Override public void run() { for(int i = 0;i<999999999;i++){ //TODO 真正的操作 } Message message = new Message(); message.what = position; message.obj = "--> "+test.get(position).toString()+" 结束"; handler.sendMessage(message); } }; pool.execute(runnable1); }这样就不会需要等待一个完成才能进行下一个,不过因为线程池的大小是5,如果开的线程大于5,其他的结果就要等待如下:
在ThreadPoolExecutor类中有几个非常重要的方法:
execute()
submit()
shutdown()
shutdownNow()
execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果
(Callable、Future和FutureTask介绍:http://www.cnblogs.com/dolphin0520/p/3949310.html )
shutdown()和shutdownNow()是用来关闭线程池的。
还有很多其他的方法:
比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等获取与线程池相关属性的方法,有兴趣的朋友可以自行查阅API。
- 【Java多线程】多线程死锁
- Java 多线程
- java 多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA多线程
- java多线程
- JAVA 多线程
- Java多线程
- java多线程
- JAVA 多线程
- Java 多线程
- Java 多线程
- java多线程
- Java 多线程
- Java多线程
- java 多线程
- if (resultCode == RESULT_OK) 在红米手机上resultCode返回并不是RESULT_OK
- 商品快照
- 单片机下使用printf的问题及替代方法
- bzoj 3864 dp套dp
- myeclipse 去掉js验证
- Java多线程
- Spring配置文件XML头部文件模板
- 关于友录项目的总结
- salesforce之apex开发1---基本类型与常用方法
- Java设计模式——适配器模式
- HDU3172-Virtual Friends
- hadoop温度排序
- [协议分析] IP分片(碎片)重组简单概念(实例)
- 面试笔试--网络知识