多线程有两种方法实现,Thread和Runnable。
在讲解怎么用之前,先讲讲为什么要使用多线程,什么时候会用上。
没使用多线程的程序,也就是普通的程序,按顺序执行一个个代码,执行完一个执行下一个。就像练习中的各种数学类程序一样。我们写完一套算法,就是希望程序一直算,算到结束,然后输出结果。
对于一条线的程序,如果使用多线程那很可能是为了节约时间,比如,1,2,3,1000000,
从1 加到一百万,输出一个结果,然后,从1乘到100输出一个结果。正常情况根据代码,先算一个,得到结果,再接着算第二个。如果使用多线程,则可以两个同时运算,然后,分别得出结果。
对于一些操作,它具备这些特征: 运行时间特别长,对输出时间没有要求,那么这个操作就可以考虑使用多线程。 准确点说,一个操作运行时间太长,影响用户体验了,如果他对输出时间没太大要求,就使用多线程,让他在单独的线程中,慢慢运行,这样解决了操作运行时间太长干扰用户体验的过程。
从这个描述看,对于一些可以在后台运行的占时间的操作,使用多线程是比较好的。
多线程的第二种情况,为了延时在Java中,用时间来确定某个行为,那非多线程的代码不可,当你的某个操作中,涉及到某个具体时间的延迟,那么多线程是不二选择。
回到文章开头使用多线程的方法:
Thread是指Thread类,一般subThread继承Thread类,然后写里面的run方法,然后将subThread的对象start。
这种方法的好处是,写一个类,里面包含的内容可以很多,进行的多线程操作也可以很复杂,这里我建议使用多线程完成,比较复杂的操作,比如创建一个新的页面,这个页面里面包含了很多东西,在用户点击某个按钮后出现。这种可以使用Thread创建,一方面,它不是第一个页面,所以不用首先建立,其次,它需要建立,所以用Thread,创造新的线程慢慢新建页面。
实现java.lang.Runnable接口,重写run()方法。
这个方法比Thread简单,相对的一般内容较小,比较适合,代码不多,耗时长的操作,比如读写文件或者数据库这一类。
这里要注意,我在类的使用讲到匿名内部类的时候,提到过多线程是最常用匿名内部类的,比如以下代码,非常简单的,创建一个运行的多线程,运行内容就是内容1.
new Handler().post(new Runnable() {
@Override
public void run() {
内容1
}
});
多线程还是对于复杂的系统才会用到的东西,而且使用多线程的难度不是创建多线程,而是维护多线程之间的关系。
单线程,每个代码的运行顺序是确定的,而多线程,代码运行的顺序是不确定的,以新建页面为例,如果用户点击打开新页面的按钮,而多线程的新建页面还未完成,那么程序就会出现bug。还有就是多个线程,同时使用一个参数,那么该参数就会面临结果不确定的特点。
面对这种情况,多线程提供了一些手段来预防,但总的来说还是考验程序员的逻辑思维,需要考虑很多情况,以及需要测试人员来辅助程序员完成极限情况的考虑。
第一种手段:synchronized 叫做加锁,或者同步锁,一般加在多线程的方法之前,它的意思是,对于许多要使用这个地方的程序,一次只能有一个线程在使用,其他线程还要用,需要等它这次运行完了之后。
举个例子,
public synchronized String getDatabase(URI uri ){
获取uri对应的数据库里面的字符串。
}
这里可能会有不同的程序想要获取数据库的参数,比如当前页面,以及新建页面都要获取。为了防止,当前页面获取了新建页面的数据库,使用synchronized ,则当前页面调用此方法之后,新建页面要获取的时候,就要等当前页面获取到了数据库的参数,该方法完成后。
还有种用法。
synchronized (a) {
内容;
a = a++;
}
这个是对一个参数进行加锁,在这个{}里面了的程序运行完之前,其他线程,这里的其他线程是包括主线程的哈。其他线程无法修改a参数。
这synchronized 都是对应实际的用途的,我觉得在实际应用中,大概有两种情况,第一,确实知道会被多次调用,于是添加。第二,跑程序的时候,发生错误,于是添加。
看上去这两种是废话,但是我们常常处于第二种情况,由于意料之外,发现居然还会出现时序错误,于是加上synchronized 解决问题。
加锁就是最佳手段,synchronized 是一种快餐型的加锁,方便,实用。
但是,多线程真正出问题,实在是非常麻烦,为了更好的控制多线程,我们有加入了lock()方法。用户手动在某个点锁死线程。它叫做ReentrantLock,它功能比synchronized 强大,可以覆盖synchronized 的所有功能,而且更加灵活,和难以使用。
所以在代码中,ReentrantLock使用几率小很多。
Private final ReentrantLock lock = new ReentrantLock
Lock.lock()
Try{
内容;
}
Finally{
Lock.unlcok();
}
这个用法和synchronized 一样,他锁住的是try里面的内容。我们也可以吧内容写出一个synchronized 的方法。然而在使用的时候,lock明显可以有更多灵活的空间。在使用的时候,从简单角度考虑,优先选择synchronized 。
在多线程中,在某个地方,写上Tread.sleep(1000)。这样的话,线程就会停止运行1000ms即1秒。这就是一个延时操作,可以让你在每个操作之间间隔1秒。
for (int i = 0; i < 100; i++) {
内容1;
Tread.sleep(1000);
}
以上就是,每一秒做一个操作,做100次。