----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
多线程
1、进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域。
2、线程:是进程中的一个执行控制单元,执行路径。是CPU调度和分派的基本单位;
一个进程中如果只有一个执行路径,这个程序称为单线程;
一个进程中有多个执行路径时,这个程序成为多线程;
3、多线程的意义
允许多个线程并发执行,提高程序运行效率。
例如:迅雷多线程下载,QQ多个人同时聊天,凌波多个人同时共享屏幕
4、CPU执行原理
class Demo extends Thread{
private String name;
Demo(String name){
this.name = name;
}
public void run(){
System.out.println("name="+"-------------"+name);
}
}
class ThreadDemo{
public static void main(String[] args) {
Demo d1 = new Demo("one");//创建了一个Thread类的子类对象
Demo d2 = new Demo("two");
d1.run();
d2.run();
for(int x=0; x<20; x++){
System.out.println("main----"+x);
}
}
}
当创建了两个线程对象d1,d2后,这时程序就有了三个线程在同时执行。
当主函数执行完d1.start(),d2.start()后,这时三个线程同时打印,结果比较杂乱,这是因为线程的随机性造成的;
随机性的原理是:windows中的多任务同时执行,其实就是多个应用程序在同时执行
而每一个应用程序都由线程来控制,所以windows就是一个多线程的操作系统,cpu是负责提供程序运算的设备。
cpu特点:在某一个时刻,只能执行一个程序,所以多个程序执行并不是真正的同时执行。其实就是cpu做这快速的
切换完成的。只是感觉上同时而已.真正意义上的同时执行需要多个CPU,也就是现在的多核;
5、线程的创建
继承Thread类:
子类覆盖父类中的run(),将线程运行的代码存放在run()中;
子类当父类用,创建子类对象就是创建一个线程;
通过调用start()开启线程;
特点:当类去描述事物,事物中有属性和行为。如果行为中有部分代码被多线程所执行,同时还在操作属性,
就需要该类继承Thread类,产生该类的对象作为线程对象。这样会导致每一个对象中都存储一份
属性数据,无法在多个线程中共享该数据,可以加上静态,虽然实现了共享但是生命周期过长;
如果一个类明确了自己的父类,就不能再继承Thread,或者一个类继承了Thread类后也同样不能继承别的
类,因为java不允许类的多继承;
new Thread(new Runnable() {
public void run(){
System.out.println("Runnable");
}
}) {
public void run() {
System.out.println("Thread");
}
}.start(); //结果:Thread
Thread类可以实现Runnable接口:Thread类中run()覆盖了Runnable接口中的run()
实现Runnable接口:
子类覆盖接口中的run();
创建Thread对象时,将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数:
这样相当于只创建一个对象,实现资源的共享,让多条线程共享一个资源;
thread类对象调用start()开启线程;
线程的五种状态:
创建:start()
临时阻塞:CPU在执行某个线程时,其它线程都不会运行,具备执行资格,但是没有执行权
冻结:sleep()或wait(),时间到notify()
运行:run()或stop()
消亡:run()方法结束
sleep()需要指定睡眠时间,单位是毫秒;
特殊的状态:临时阻塞。具备了执行资格,但是还没有获取资源;
6、线程安全
原因:多个线程并发操作同一对象,访问出现延迟。
线程随机性,一条线程的操作还没有结束,CPU切换到另一条线程工作,导致操作出错
线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响非常大;
7、解决线程安全问题
解决原理:让多条操作共享数据的代码在某一时间段被一个线程执行,执行过程中其它线程不参与运算;
同步代码块: synchronized(对象) {
需要同步的代码;
}
将访问数据的代码锁住,在同步代码块中的内容同一时间内只能一个线程执行;
同步可以解决安全问题的根本原因就在那个对象上;该对象如同锁的功能;
同步方法:方法上加synchronized,整个方法的代码都是同步的,只能一个线程运行,同步方法使用this作为锁;
同步的前提:要两个或者两个以上的线程;多个线程使用的是同一个锁;
同步的好处:同步的出现解决了多线程的安全问题;
同步的弊端:当线程相当多时,每个线程都会去判断同步上的锁耗费资源,降低程序的运行效率;
死锁:在多个线程并发执行使用多个锁来同步时,有可能互相冲突,导致程序无法继续执行(同步嵌套);
同步出现解决了线程安全问题,但是非常消耗资源,效率会降低并且容易引发死锁;
8、线程间通信
在同步代码中可以使用锁对象的wait()方法让当前线程等待;
使用锁对象的notify()方法可以将正在等待的线程唤醒;
如果多个线程都在等待,notify()唤醒随机1个;
notifyAll()方法可以唤醒所有在等待的线程;
wait(),notify(),notifyAll(),这些用来操作线程存在与同步中,使用这些方法时必须要标识所属的同步的锁,
锁可以是任意对象,所以任意对象调用的方法一定定义Object类中;
wait(),sleep()区别:
wait():释放cpu执行权,释放锁;
sleep():释放cpu执行权,不释放锁;
9、JDK5之后的线程同步与通信
同步:
使用java.util.concurrent.locks.Lock接口的实现类对象来进行同步;
ReentrantLock就是Lock的实现类,可以实现synchronized的功能;
在需要同步的代码块前后使用lock()和unlock()方法来完成同步;
unlock()最好放在finally中,因为如果上面代码抛出异常没有解锁;,会导致其它线程无法运行,程序卡死;
通信:
使用Lock对象的newCondition()方法获取一个Condition对象,Condition对象可以控制指定线程的等待与唤醒;
await()方法可以控制线程等待;
signal()方法可以唤醒等待的线程;
signalAll()方法可以唤醒所有等待线程;
10、停止线程
定义循环结束标记:因为线程运行代码一般都是循环,只要控制了循环即可;
使用interrupt()中断:该方法是结束线程的冻结状态,使线程回到运行状态中来;
注:stop()已经过时不再使用;
11、线程中方法
currentThread():静态方法,返回当前线程对象的引用;
getName(),setName():用来获取、设置当前线程的名字;
sleep():控制线程休眠,单位为毫秒;
setDaemon():将线程设置为守护线程。线程默认是非守护线程,守护线程不能单独执行;
join():让出执行权,当前线程暂停,等待加入的线程运行结束,当前线程再执行;
setPriority(int num):设置线程优先级;
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组;
Thread(String name):构造函数,线程对象一建立就可以指定名称;
setName(String name):自定义线程名字