------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1. 多线程
●进程
是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
●线程(例:FlashGet)
◆就是进程中一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程
◆JVM启动的时候会有一个进程java.exe,该进程中至少一个线程负责java程序的运行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
◆扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程
◆可以形象的把多线程的运行行为在互相抢夺cpu的执行权
这就是多线程的俄一个特性:随机性,谁抢到谁执行,至于执行多长,cpu说了算
◆为什么覆盖run方法
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法
◆static Thread currentThread();获取当前线程对象
getName();获取线程名称
设置线程名称:setName或者构造函数
●多线程存在的意义
●线程的创建方式
◆如何在自定义的代码中,自定义一个线程呢
通过API的查找,java已经提供了对线程这类事物的描述,就是Thread类,
创建线程的第一种方式,继承Thread类
★步骤:
(1)定义类继承Thread
(2)复写Thread类中的run方法
(3)调用线程的start方法,该方法两个作用:启动线程,调用run方法
创建线程的第二种方式,实现Runnable接口
★步骤:
(1)定义了实现Runable接口
(2)覆盖Runable接口中的run方法,将线程要运行的代码存放在该方法中
(3)通过Thread类建立线程对象
(4)将Runnable接口的子类对象作为对象作为实际参数传递给Thread类的构造函数
(5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
★★★实现方式和继承方式有什么区别
实现方式:避免了单继承的局限性,定义线程时建议使用实现方式
★两种方式区别:
继承Thread:线程代码存放在Thread子类的run方法中
实现Runnable:线程代码存放在接口的子类的run方法中
●多线程安全
★原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
★解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。java对于多线程的安全问题提供了专业的解决方式:同步代码块
synchronized(对象){ }
对象如同锁,持有锁的线程可以在同步中执行
★同步的前提:
(1)必须要有两个或者两个以上的线程
(2)必须是多个线程使用同一个锁
★必须保证锁里面只有一个锁在运行
★好处:解决了多线程的安全问题
★弊端:多个线程需要判断锁,较为消耗资源
★同步函数用的是哪一个锁?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this
★静态进内存时:内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class,该对象的类型是Class
★静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class
★单例模式懒汉式用于实例的延时加载如果多线程访问会出现安全问题 可以加同步来解决,低效解决方法 双重判断 使用的锁是该类所属的字节码文件
★死锁
摘自网路
public class DeadLockTest implementsRunnable{
private int flag;
static Object o1 = new Object(), o2 = newObject(); //静态的对象,被DeadLockTest的所有实例对象所公用
public void run(){
System.out.println(flag);
if(flag == 0){
synchronized(o1){
try{
Thread.sleep(500);
} catch(Exception e){
e.printStackTrace();
}
synchronized(o2){
}
}
}
if(flag == 1){
synchronized(o2){
try{
Thread.sleep(500);
} catch(Exception e){
e.printStackTrace();
}
synchronized(o1){
}
}
}
}
public static void main(String[] args){
DeadLockTest test1 = new DeadLockTest();
DeadLockTest test2 = new DeadLockTest();
test1.flag = 1;
test2.flag = 0;
Thread thread1 = new Thread(test1);
Thread thread2 = new Thread(test2);
thread1.start();
thread2.start();
}
}
解释:在main方法中,实例化了两个实现了Runnable接口的DeadLockTest对象test1和test2,test1的flag等于1,所以在thread1线程执行的时候执行的是run()方法后半部分的代码,test2的flag等于2,所以在thread2线程启动的时候执行的是run()方法前半部分的代码,此时,出现了下列现象:thread1线程占有了o1对象并等待o2对象,而thread2线程占有了o2对象并等待o1对象,而o1和o2又被这俩个线程所共享,所以就出现了死锁的问题了。
●线程间通讯:
其实就是多个线程在操作同一个资源,但是操作的动作不同
★等待唤醒机制
wait() notify() notifyAll()
都是用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁的概念
★为什么这些操作线程的方法要定义object类中?
因为这些方法在操作同步中线程时,都必须要标识他们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在object类中
★调用某个对象的wait()方法,相当于让当前线程交出次对象的monitor,然后进入等状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
★notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知
★同样地,调用某个对象的notify()方法,当前线程也必须用有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法中)
★多个生产者,多个消费者 while(flag) notifyAll()
★JDK1.5中提供了多线程升级解决方案,将同步中Synchronized替换成现实Lock操作
将object中的wait notify notifyAll 替换为Condition对象,该对象可以用Lock锁,进行获取
●停止线程
★ 定义循环结束标记
因为线程运行代码一般都是循环,只要控制了循环即可
★使用interrupt(中断)方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
注:stop方法已经过时不在使用
★如何停止线程
只有一种,run犯法结束。开启多线程运行,运行代码通常是循环结构
只要控制住循环,就可以让run方法结束,也就是线程结束
★特殊情况
当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束
当没有制定的方式让冻结的线程恢复到运行状态时,这是需要对冻结进行清除。
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束
Thread()类提供的方法:interrupt()
★守护线程 (后台线程)setDaemon();
★join:当A线程执行了B线程的.join()方法A线程就会等待。等B线程都执行完,A才会执行
join可以用来临时加入线程