------------android培训、java培训、期待与您交流! ----------
线程
什么是线程:
专业定义:
利用对象,可将一个程序分割成相互独立的区域。我们通常也需要将一个程序转换成多个独立运行的子任
务。
象这样的每个子任务都叫作一个“线程”(Thread)。编写程序时,可将每个线程都想象成独立运行,而且
都有自己的专用 CPU。一些基础机制实际会为我们自动分割 CPU 的时间。我们通常不必关心这些细节问题,
所以多线程的代码编写是相当简便的。
这时理解一些定义对以后的学习狠有帮助。“进程”是指一种“自包容”的运行程序,有自己的地址空间。
“多任务”操作系统能同时运行多个进程(程序)——但实际是由于 CPU 分时机制的作用,使每个进程都能
循环获得自己的 CPU 时间片。但由于轮换速度非常快,使得所有程序好象是在“同时”运行一样。“线程”
是进程内部单一的一个顺序控制流。因此,一个进程可能容纳了多个同时执行的线程。
通俗定义:
线程是一个程序里的不同执行路径
为什么需要线程:
线程的使用和请注意事项:
进程太浪费资源,进程切换需要花时间
单线程:
程序有一个入口一个出口和一个顺序执行的序列,程序执行过程的任何时刻都只有一个单独的执行点
多线程:
多线程的优势:在单个程序内部在同一时刻可以进行多种运算
多线程编程简单,高效(能直接共享数据和资源,多进程不能)
适合开发服务程序(web服务,聊天服务等)
创建线程的方法:
线程的控制:
为创建一个线程,最简单的方法就是从 Thread 类继承。这个类包含了创建和运行线程所需的一切东西。
Thread 最重要的方法是 run()。但为了使用 run(),必须对其进行过载或者覆盖,使其能充分按自己的吩咐
行事。因此,run()属于那些会与程序中的其他线程“并发”或“同时”执行的代码。
第一种:
创建一个继承Thread的类A,并重写从Thread继承来的run方法,
构造一个A类对象aa,
调用aa对象的start方法(start方法从Thread继承来,会创建一个新线程,并自动调用aa对象的run方法).
注意:直接调用run方法不会创建线程执行一个线程就是执行该线程的run方法
执行完aa.start()表示aa线程具有了可以立即被cpu执行的资格,但由于抢占cpu执行的线程很多,cpu不一定立即执行aa线程
一个Thread对象只能代表一个线程,不能调用两次start方法
第二种:
定义一个实现Runnable接口的类A,创建A类对象aa,
利用aa构造一个Thread对象tt ,(Thread中的一个构造方法public Thread(Runnable r))Thread tt = new Thread(aa);
调用tt中的start方法.注意:Runnable接口中只有一个void run()方法
Thread类中的常用方法
public final void setName(Sting name)设置当前线程的名字
public static Thread currentThread()返回对当前正在执行的线程的对象的引用 public final String getName()返回当前线程的名字
线程的优先级:线程的三个状态:就绪状态,运行状态,阻塞状态
线程控制的基本方法
isAlive()判断线程是否还未终止(三个状态都是活着)
getPriority()获得线程的优先级
setPriority()设置线程的优先级
Thread.sleep()让当前线程睡眠指定毫秒(由运行到阻塞)
join()调用某线程的该方法,将当前线程与该线程合并,即等待该线程结束,再恢复当前线程的运行
yield()让出cpu,当前线程进入就绪队列等待调度(由运行到就绪)
wait()当前线程进入对象wait pool
notify()/notifyAll() 唤醒对象的wait pool中的一个/所有等待进程
线程的优先级从1到10,主线程的缺省优先级是5,子线程的优先级默认和其父线程相同 java提供一个线程调度器监控程序启动后进入就绪状态的所有线程,按线程优先级决定执行顺序
Thread.MIN_PRIORITY = 1
Thread.NORMAL_PRIORITY = 5
Thread.MAX_PRIORTIY = 10
获得和设置线程对象的优先级
int getPriority();
void setPriority(int newPriority)
通常优先级高的线程先于优先级低的线程执行,但不总是这样,实际开发中并不单纯依赖优先级来决定线程运行次序
线程的休眠:
- 暂停执行当前运行的线程,使之进入阻塞状态,经过指定的延迟时间后再醒来并进入到就绪状态
- Thread类提供的方法
- public static void sleep(long millis)
- public static void sleep(long millis, int
- 会抛出InterruptedException异常,必须用try catch捕获子类继承Thread类或实现Runnable接口的run方法都不能抛出异常
为何会堵塞
- 堵塞状态是前述四种状态中最有趣的,值得我们作进一步的探讨。线程被堵塞可能是由下述五方面的原因造
- 成的:
- 调用 sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。
- 用 suspend()暂停了线程的执行。除非线程收到 resume() 消息,否则不会返回“可运行”状态。
- 用 wait()暂停了线程的执行。除非线程收到 nofify() 或者 notifyAll()消息,否则不会变成“可运行”
- 线程正在等候一些 IO(输入输出)操作完成。
- 线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。
死锁:
有两个线程,彼此都需要对方的资源才可以运行,但双方都不愿意先让出自己的资源让对方运行,导致双方都在等待,一直得不到运行。
线程的让步:
让出cpu,给其他线程执行的机会
运行中的线程主动放弃当前获得cpu处理的机会,进入就绪状态
public static void yield()
线程的串行化:
在多线程程序中,如果一个线程运行的过程用到了另一个线程的运行结果,则可以进行线程的串行化处理
public final void join() throws InterruptedException
t.join()暂停执行t.join()的这个线程,等待t线程运行完毕,这个线程才会获得继续执行的机会
线程的同步:
synchronized关键字
synchronized可以修饰一个方法或方法内的某个代码块
synchronized修饰代码块
格式:
synchronized(随便一个类对象名aa)
{
同步代码块
}
说明:
synchronized修饰方法:判断aa是否被其他线程霸占,如果发现已霸占,当前线程等待,如果没有被霸占当前线程霸占aa对象,执行同步代码块,执行完自动释放对aa的霸占,线程再次竞争对aa的霸占,cpu会选择一个线程霸占aa
结果:一个线程操作某资源时,不允许其他线程操作该资源,即一次只允许一个线程处理该资源
synchronized修饰方法时,实际霸占的是该方法的this指针所指向的对象,即正在调用该方法的对象
霸占的专业术语叫锁定,霸占住对象专业术语叫监听器
疑:买票程序用expends thread方法能不能把类里要锁定的字符串设置为数据区的字符串|String str = "aaa";不加static
卖票程序:
class A implements Runnable { public int ticket = 100; String str = new String("哈哈"); public void run() { String str = "哈哈"; while(true) { synchronized(str) // // synchronized 保证了当前线程没执行完13行到23行的代码时,其它线程不能访问该代码 { if(ticket > 0) { System.out.printf("%s线程正在卖出第%d张票\n",Thread.currentThread().getName(),ticket); --ticket; } else { break; } } } } } public class TestTickets_8 { public static void main(String[] args) { A aa = new A(); Thread t1 = new Thread(aa); t1.start(); Thread t2 = new Thread(aa); t2.start(); } }