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类中有几个非常重要的方法:

1
2
3
4
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。





1 0
原创粉丝点击