本帖最后由 xiao_ming 于 2015-11-12 10:06 编辑
第Ⅱ期Android菜鸟饭团#Java学习#第十八课 活动笔记
一、线程的概念:
1、线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。
2、线程与进程: 1)一个进程包括由操作系统分配的内存空间,包含一个或多个线程; 2)一个线程不能独立的存在,它必须是进程的一部分; 3)一个进程一直运行,直到所有的非守候线程都结束运行后才能结束; 4)多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度。
二、线程的创建:
1、一个线程的生命周期: 1)新状态: 一个新产生的线程从新状态开始了它的生命周期。它保持这个状态知道程序start这个线程; 2)运行状态:当一个新状态的线程被start以后,线程就变成可运行状态,一个线程在此状态下被认为是开始执行其任务; 3)就绪状态:当一个线程等待另外一个线程执行一个任务的时候,该线程就进入就绪状态。当另一个线程给就绪状态的线程发送信号时,该线程才重新切换到运行状态; 4)休眠状态: 由于一个线程的时间片用完了,该线程从运行状态进入休眠状态。当时间间隔到期或者等待的时间发生了,该状态的线程切换到运行状态; 5)终止状态: 一个运行状态的线程完成任务或者其他终止条件发生,该线程就切换到终止状态;
2、线程的创建: 1)实现Runable接口; 2)通过继承Thread类; 2.1、实现Runable接口: 1)为了实现Runnable,一个类只需要执行一个方法调用run(),声明如下: publicvoid run(); 2)你可以重写该方法,重要的是理解的run()可以调用其他方法,使用其他类,并声明变量,就像主线程一样; 3)在创建一个实现Runnable接口的类之后,你可以在类中实例化一个线程对象; 4)Thread定义了几个构造方法,下面的这个是我们经常使用的: Thread(Runnable threadOb,String threadName); 5)这里,threadOb 是一个实现Runnable 接口的类的实例,并且 threadName指定新线程的名字; 6)新线程创建之后,你调用它的start()方法它才会运行。 void start(); 2.2、继承Thread类: 创建一个新的类,该类继承Thread类,然后创建一个该类的实例。 继承类必须重写run()方法,该方法是新线程的入口点。它也必须调用start()方法执行 // 通过继承 Thread 创建线程 class NewThread extends Thread {
NewThread() {
// 创建第二个新线程
super("Demo Thread");
System.out.println("Child thread: " + this);
start(); // 开始线程
}
// 第二个线程入口
public void run() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Child Thread: " + i);
// 让线程休眠一会
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");
}
}
public class ExtendThread {
public static void main(String args[]) {
new NewThread(); // 创建一个新线程
try {
for(int i = 5; i > 0; i--) {
System.out.println("Main Thread: " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
} |
运行结果如下:
Child thread: Thread[Demo Thread,5,main] Main Thread: 5 Child Thread: 5 Child Thread: 4 Main Thread: 4 Child Thread: 3 Child Thread: 2 Main Thread: 3 Child Thread: 1 Exiting child thread. Main Thread: 2 Main Thread: 1 Main thread exiting. |
四、sleep、join、yeild方法: 1.sleep方法: sleep是Thread类的静态方法。sleep的作用是让线程休眠制定的时间,在时间到达时恢复,也就是说sleep将在接到时间到达事件事恢复线程执行,例如:
try{ System.out.println("I'm going to bed"); Thread.sleep(1000); System.out.println("I wake up"); } catch(IntrruptedException e) { } |
2.join方法: 线程实例的方法join()方法可以使得一个线程在另一个线程结束后再执行。如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。 Waits for this thread to die. public final void join() throws InterruptedException 在join()方法内设定超时,使得join()方法的影响在特定超时后无效。当超时时,主方法和任务线程申请运行的时候是平等的。然而,当涉及sleep时,join()方法依靠操作系统计时,所以你不应该假定join()方法将会等待你指定的时间。 3.yeild方法: 理论上,yield意味着放手,放弃,投降。一个调用yield()方法的线程告诉虚拟机它乐意让其他线程占用自己的位置。 Yield是一个静态的原生(native)方法 Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。 Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态 五、线程同步: 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。 1、锁的原理 Java中每个对象都有一个内置锁 1)当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。 当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。 2) 一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。 3)释放锁是指持锁线程退出了synchronized同步方法或代码块。 2、关于锁和同步,有一下几个要点: 1)只能同步方法,而不能同步变量和类; 2)每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步? 3)不必同步类中所有的方法,类可以同时拥有同步和非同步方法。 4)如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。 5)如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。 6)线程睡眠时,它所持的任何锁都不会释放。 7)线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。 8)同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。 9)在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。 例如: public int fix(int y) { synchronized (this) { x = x - y; } return x; } |
3、当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:
public synchronized int getX() { return x++; } 与 public int getX() { synchronized (this) { return x; } } |
效果是完全一样的。
4、静态方法同步 要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类 例如:
public static synchronized int setName(String name){ Xxx.name = name; } 等价于 public static int setName(String name){ synchronized(Xxx.class){ Xxx.name = name; } } |
5、线程同步小结: 1)线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。 2)线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。 3)对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。 4)对于同步,要时刻清醒在哪个对象上同步,这是关键。 5)编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。 6)当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。 7)死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
每一个技术小白都有一个成为大神的梦想,现在Android菜鸟饭团就给你这个成就梦想的机会。我们提供最新的Android技术教学,只要你又耐心和毅力就一定会在这里有所收获。 Android菜鸟饭团由 南阳GDG组织发起,秉承着开放、分享、创新的原则,希望通过GDG社区的力量能够给更多的想要学习Android开发技术的小白们创造一个学习,交流,分享的环境。同往常的GDG活动一样,我们依然是任性的一个子都不要,并且还在周六的分享中提供盒饭和不定期的惊喜小礼物呦~所以快来加入我们吧,为你的大神梦想迈出第一步。
|