迅雷2014校招笔考题之多线程编程
题意:
大体如下:android中Activity开启两个线程,一个Produce线程,一个Customer线程,共享Integer[10]数组,Produce线程不断向数组中写入1000,写满后等待,Customer线程不断清空数组内容,当数组全被清空后,通知Produce线程写入数据,Activity要及时刷新当前Integer数组内容size。
分析:
从题意可知,在Java方面,这里涉及到两个线程共享一个数组,也就是说当一个线程在运行操作数组时,另一个线程只能等待,否则将出现竞争状态,即一个线程在运行时,另一个线程只能处于阻塞状态。在Andriod方面,因为在Acitivty中要即使刷新当前Integer数组的内容size,所以在UI主线程中要实时读取Integer的size,这个读取是在主线程完成的,而两个子线程可以把改变的信息通过handler发送到主线程,进而主线程进行解析信息,根据信息显示。
实现:
分两种方法实现,一种为比较旧的版本,用到了java旧版本的监视器,另一种使用较新版本的类,这些类内部具有阻塞功能,三个具体的阻塞队列为:ArrayBlockingQueue, LinkBlockingQueue和PriorityBlockingQueue。它们都在java.util.concurrent的包中。其中,ArrayBlockingQueue使用数组实现阻塞队列。LinkBlockingQueue使用链表实现阻塞队列。
1 利用监听器(monitor)对象:
在activity的xml布局中定义了两个TextView,分别用来显示实时数组大小和写入清楚情况:
<?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="wrap_content" android:orientation="vertical" > <TextView android:id = "@+id/size" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="20.0sp" android:padding="5dp" /> <ScrollView android:id="@+id/scrollView01" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/produce_comsumer" android:gravity="center_vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </ScrollView> </LinearLayout>
定义了一个类Buffer,完成包括write(写入方法)和read(清除方法)。程序中生成了两个状态,分别为notFull和notEmpty,作用具体看程序注解。而uiHandler主要用于主线程和子线程的消息通信。当子线程发生变化之后,通过sendMessage方法发生消息,然后主线程通过CallBack得到Message
package gibbon.thread.threadtestinandroid; import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import android.R.integer; import android.os.Message; public class Buffer { private static Lock lock = new ReentrantLock(); private static Condition notEmpty = lock.newCondition(); private static Condition notFull = lock.newCondition(); private static final int CAPACITY = 10; protected LinkedList<Integer> queue = new LinkedList<Integer>(); protected void write(int value){ lock.lock(); try { if(queue.size() == CAPACITY){ System.out.println("Wait for not noFull condition"); Message msg = new Message(); msg.what = Definition.FULL; ConsumerProducerActivity.uiHandler.sendMessage(msg); notFull.await(); //因为已经满了,所以notFull必须等待,等取走数据才能继续运行(即只能等待下面的数据取走,然后调用signal唤醒) } queue.offer(value); Message msg = new Message(); msg.what = Definition.NOTEMPTY; ConsumerProducerActivity.uiHandler.sendMessage(msg); notEmpty.signal(); //因为非空,所以唤醒非空功能 } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); }finally{ lock.unlock(); } } protected int read(){ lock.lock(); int value = 0; try { if(queue.size() == 0){ System.out.println("\t\tWait for notEmpty condition"); Message msg = new Message(); msg.what = Definition.Empty; ConsumerProducerActivity.uiHandler.sendMessage(msg); //这里的Message对象在线程中只能多次创建,若不这样, //则如在主线程用到了该对象的同时,下面的线程又进行了修改,则就发生变化 notEmpty.await(); //这里就可以等待上面的数据写入之后,通过notEmpty来唤醒 } value = queue.remove(); Message msg = new Message(); msg.what = Definition.NOTEMPTY; ConsumerProducerActivity.uiHandler.sendMessage(msg); notFull.signal(); //取走数据了,换新上面的notFull等待 } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); }finally{ lock.unlock(); } return value; } }
然后在acitivty中启动Producer和Customer两个线程,为了方便,只循环了50次。
package gibbon.thread.threadtestinandroid; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.os.Bundle; import android.os.Handler; import android.os.Handler.Callback; import android.os.Message; import android.R.integer; import android.app.Activity; import android.view.Menu; import android.widget.TextView; public class ConsumerProducerActivity extends Activity { protected static Handler uiHandler; //protected static Handler comsumerHandler; private TextView sizeTextView; private TextView producer_consumer; private ExecutorService executorService; private static String str; private static Buffer buffer = new Buffer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_consumer_producer); uiHandler = new Handler(new UiHandler()); init(); } private void init(){ sizeTextView = (TextView)findViewById(R.id.size); producer_consumer = (TextView)findViewById(R.id.produce_comsumer); executorService = Executors.newFixedThreadPool(2); executorService.execute(new ProducerTask()); executorService.execute(new ComsumerTask()); executorService.shutdown(); } public static class ProducerTask implements Runnable{ @Override public void run(){ int count = 50; while(count-- > 0){ buffer.write(1000); try { Thread.sleep((int)(Math.random()*10000)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static class ComsumerTask implements Runnable{ @Override public void run(){ int count = 50; int value = 0; while(count-- > 0){ buffer.read(); try { Thread.sleep((int)(Math.random()*10000)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.consumer_producer, menu); return true; } public class UiHandler implements Callback{ @Override public boolean handleMessage(Message msg) { // TODO Auto-generated method stub sizeTextView.setText("大小为:" + buffer.queue.size()); synchronized (msg) { switch (msg.what) { case Definition.Empty: str += Definition.EMPTY_STRING; str += "\n"; break; case Definition.FULL: str += Definition.FULL_STRING; str += "\n"; break; case Definition.NOTEMPTY: str += Definition.NOTEMPTY_STRING; str += "\n"; break; case Definition.NOTFULL: str += Definition.NOTFULL_STRING; str += "\n"; break; default: break; } } producer_consumer.setText(str); return false; } } }
另外附带一些自定义变量:
package gibbon.thread.threadtestinandroid; import android.R.integer; public class Definition { protected static final int NOTFULL = 1; protected static final int NOTEMPTY = 2; protected static final int BUFFERSZIE = 3; protected static final int FULL = 4; protected static final int Empty = 5; protected static final String NOTFULL_STRING = "Wait for not noFull condition"; protected static final String NOTEMPTY_STRING = "\tWait for notEmpty condition"; protected static final String FULL_STRING = "\t\tBufferSize FuLL"; protected static final String EMPTY_STRING = "\t\t\tBufferSize Empty"; }
运行结果如下:
2 使用ArrayBlockingQueue:
程序如下:
public class ConsumerProducerActivity extends Activity { protected static Handler uiHandler; private TextView sizeTextView; private TextView producer_consumer; private ExecutorService executorService; private static String str; private static Buffer buffer = new Buffer(); private static ArrayBlockingQueue<Integer> integer_buffer = new ArrayBlockingQueue<Integer>(10); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_consumer_producer); uiHandler = new Handler(new UiHandler()); init(); } private void init(){ sizeTextView = (TextView)findViewById(R.id.size); producer_consumer = (TextView)findViewById(R.id.produce_comsumer); executorService = Executors.newFixedThreadPool(2); //executorService.execute(new ProducerTask()); //executorService.execute(new ComsumerTask()); executorService.execute(new Producer()); executorService.execute(new Consumer()); executorService.shutdown(); } public static class Producer implements Runnable{ @Override public void run(){ try { int i = 50; while(i-- >0){ if(integer_buffer.size() == 10){ Message msg = new Message(); msg.what = Definition.FULL; uiHandler.sendMessage(msg); } integer_buffer.put(1000); Message msg = new Message(); msg.what = Definition.NOTEMPTY; uiHandler.sendMessage(msg); Thread.sleep((int)(Math.random()*10000)); } } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } public static class Consumer implements Runnable{ @Override public void run(){ try { int i = 50; while(i-- >0){ if(integer_buffer.size() == 0){ Message msg = new Message(); msg.what = Definition.Empty; uiHandler.sendMessage(msg); } integer_buffer.take(); Message msg = new Message(); msg.what = Definition.NOTFULL; uiHandler.sendMessage(msg); Thread.sleep((int)(Math.random()*10000)); } } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.consumer_producer, menu); return true; } public class UiHandler implements Callback{ @Override public boolean handleMessage(Message msg) { // TODO Auto-generated method stub sizeTextView.setText("大小为:" + integer_buffer.size()); switch (msg.what) { case Definition.Empty: str += Definition.EMPTY_STRING; str += "\n"; break; case Definition.FULL: str += Definition.FULL_STRING; str += "\n"; break; case Definition.NOTEMPTY: str += Definition.NOTEMPTY_STRING; str += "\n"; break; case Definition.NOTFULL: str += Definition.NOTFULL_STRING; str += "\n"; break; default: break; } producer_consumer.setText(str); return false; } } }这个比较容易理解,因为ArrayBlockingQueue具有能自行实现阻塞队列