今天有需求接触一下多线程。没解决了问题。不过我还是说出来我们的问题。
我们有一个无线摄像头。要通过android手机查看摄像头的画面。
第一,需要知道摄像头,即使是视频,但也是一帧一帧的画面组成的。
第二,在android手机中显示的方式,我知道的有两种:一种就是在WebView上显示,一种是在SurfaceView上。我第一就想到了SurfaceView,因为这货是专门应用于游戏画面的料,以前搞二维码的时候涉及到手机摄像头那方面的时候接触过一点,有所领略。
WebView的话,我同学实现了,他推荐我,他的过程异常简单顺畅,只需获取到摄像头画面的IP,注意我说的是摄像头的画面地址,然后用webview.loadURL(URL);方法就可以实现了。为什么我没有实现呢,我个人觉得,只是可能的问题,我也不太懂:1,摄像头不一样,他是成品,是从淘宝买的那种一搜“无线摄像头”或“ip摄像头”就出来的摄像头;我们的摄像头是自己单片机上安装的真的是摄像头=
=。。一个摄像头模块,无线通信部分的模块还得自己搭配的;2,是我们的登录到路由器的地址后(需要密码才能进入,密码取不掉),还须跳转页面摄像画面;3,是我们的摄像头画面在webview上显示不出来(这是最坑爹的),下了一个firefox的安卓版后,打开可以显示,我不明白webview上点击链接什么的是通过webkit内核来解析还是怎么着,反正是不显示。后来我通过查看其整个http通信细节,才取到一帧一帧的画面,又通过线程在surfaceview上画了出来,肯定比在电脑上顿,没电脑上那么流畅。怎么破?老大说用双线程,一个线程取bitmap,一个线程用于画画,利用用await()和signal()。
多线程,以前说实话没应用过,只知道买火车票n个窗口之类的。那么索性借机了解一下,再说得实现啊,有利于优化的,就得搞。
海搜,看了好多。
最后自己跟踪debug看了几次,总结如下。
先上自己的demo,改于别人的~:
大体说一下,我这篇demo的意思是,两个线程,一个增加hello,一个说hello,但是增加hello达不到指标AIM的话是不能说出hello的。
public class Test2 { private static final int AIM = 10; private static int i = 0; private static IWantHello hello = new IWantHello(); static boolean flag = true; public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); executor.execute(new SayHello()); executor.execute(new AddHello()); } public static class AddHello implements Runnable { @Override public void run() { while (flag) { try { hello.addHello(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static class SayHello implements Runnable { @Override public void run() { while (flag) { flag = hello.sayHello(); if(!flag){ System.out.println("说过我就不说了!"); break; } } } } public static class IWantHello { private static Lock l = new ReentrantLock(true); // 创建一个锁 private static Condition c1 = l.newCondition(); // 创建一个条件 public void addHello() throws InterruptedException { l.lock(); // 获得锁 try { if (i == AIM) { System.out.println("达到了目标"); c1.signal(); } else { i++; System.out.println("还没到,继续add"); } } finally { l.unlock(); } } public boolean sayHello() { l.lock();//备注1 try { if (i < AIM) { c1.await(); System.out.println("我能被打印出来说明我(本线程)被addHello中的signal唤醒!"); } else { System.out.println("终于可以说hello了!"); flag = false; return flag; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { l.unlock(); } return true; } } }
加锁与不加锁的不同就在于,加锁后,线程体内的代码可以一气执行完,又跟单线程似的,而不加锁的话,两个线程体中的代码是乱序执行的,通常是交替执行,或者一个执行了两行一个执行了一行之类的执行方式。想学好线程,最好是自己加断点,跟着debug走,走几次就明白了。unlock()要放到finally里面。
至于await()和signal(),哪里不能执行或者暂时不需要执行或者没满足条件不能执行的地方之前,你加一个await(),让await()后面的代码处于等待状态,等到什么时候呢?等signal()的唤醒,那么哪里加signal()呢,哪里眼看可以满足await()后面的代码执行要求,那么就加到哪里,具体跑一遍我的demo,再坐几个断点,你就全明白了。
讲一下本demo,最好在debug模式下运行本demo,不然AIM=10太少了,或者你修改成10000.
本demo中
从main开始,首先执行的是sayHello这个方法,因为sayHello在备注1处上了锁,而且解锁在finally,整个程序的最后,所以一直执行sayHello这个方法里的代码。
第一次判断,i<AIM,执行await(),则该线程处于等待状态,awai()后面的代码不再执行,处于等待执行状态。
这时候执行跳到addHello这个方法,这个方法也加了锁,所以顺序执行到解锁处。由于i一直处在小雨AIM的情况下,所以一直都不会运行i==AIM里的signal()方法,所以sayHello一直都不会被唤醒,直到i=10的时候,与AIM相等了,执行signal,唤醒sayHello,然后执行到解锁后,开始执行之前await后面的代码,即“我能被打印出来说明我(本线程)被addHello中的signal唤醒!”
这就是整个程序的过程,我还是建议看本文的你能自己跑一遍,在debug模式中跑一遍就会加深理解了。相信我,不会线程的同学们。