多线程
1、线程
宏观来讲
进程:就是正在运行的程序
线程:就是进程的执行路径,执行单元
2、创建一个线程的两种方式
1、定义一个类继承Thread类
public class A extends Thread{
}
new A().start();
2、定义一个类实现Runnable接口,并且重写run()方法
public class A implements Runnable{
@Override
public void run(){
}
}
new Thread(new A()).start();
3、线程的随机性原理
多个程序实际是通过CPU在做高效切换实现的
4、线程的生命周期
2、锁,同步代码块,同步方法
1、同步代码块
synchronized(锁对象){
需要被锁的代码
}
注意
多个线程必须使用同一个锁对象,要不然锁无效
2、同步方法
public synchronized void show(){} //普通方法的锁是this
public static synchronized void show(){} //
静态方法的锁是当前类的字节码文件对象
类名.class
3、注意问题
多个线程必须使用同一个锁对象,要不然锁无效
同步代码块锁可以是任意对象
同步方法的锁是this
静态方法的锁是当前类的字节码文件对象:类名.class
4、什么时候用同步代码块,什么时候用同步方法
尽可能用同步代码块
如果一个方法内的所有代码都被同步代码块包住了,那就用同步方法就可以了
3.死锁案例
死锁是进程死锁的简称,是由Dijkstra于1965年研究银行家算法时首先提出来的。它是计算机操作系统乃至并发程序设计中最难处理的问题之一。实际上,死锁问题不仅在计算机系统中存在,在我们日常生活中它也广泛存在。
1.产生死锁的必要条件
如果在计算机系统中同时具备下面四个必要条件时,那麽会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。
1.互斥条件。即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥。
2.不可抢占条件。进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。
3.占有且申请条件。进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。
4.循环等待条件。存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。
上面我们提到的这四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。
2.死锁的预防
如果在计算机系统中同时具备下面四个必要条件时,那麽会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。
1.互斥条件。即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥。
2.不可抢占条件。进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。
3.占有且申请条件。进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。
4.循环等待条件。存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。
上面我们提到的这四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。
2.死锁的预防
前面介绍了死锁发生时的四个必要条件,只要破坏这四个必要条件中的任意一个条件,死锁就不会发生。这就为我们解决死锁问题提供了可能。一般地,解决死锁的方法分为死锁的预防,避免,检测与恢复三种(注意:死锁的检测与恢复是一个方法)。死锁的预防是保证系统不进入死锁状态的一种策略。它的基本思想是要求进程申请资源时遵循某种协议,从而打破产生死锁的四个必要条件中的一个或几个,保证系统不会进入死锁状态。
4、多个线程操作同一数据的问题
1、问题产生原理分析
注意:每个线程有个普通的成员变量Student,创建线程的时候需要先初始化该变量
2、解决方案
1、看有没有共享数据
2、看操作共享数据的语句是不是多条语句
3、看是不是在多线程的环境中
最后,把操作共享数据的多条语句用锁 锁起来
所以给两个线程while(true){}中间的内容加锁
5、等待唤醒机制
两个线程共用一把锁,此时可以调用该锁的wait和notify方法,实现等待唤醒机制
2、IllegalMonitorStateException异常原因及解决办法
如果当前的线程不是此对象锁的所有者,缺调用该对象的notify(),notify(),wait()方法时抛出该异常
换句话说就是当前线程中的同步代码块的锁 和 调用这三个方法的锁对象不一致就会报错,例如
synchronized(Student.class){
Object.class.notify();
}
3、sleep和wait的区别
wait:是Object类的方法,可以不用传递参数,释放锁对象sleep:是Thread类的静态方法,需要传递参数,不释放锁对象
6、线程的优先级(Thread类中)
1、线程优先级级别
线程默认优先级是5。范围是1-10
Thread.MAX_PRIORITY //10
Thread.MIN_PRIORITY //1
Thread.NORM_PRIORITY //5
2、方法
public final int getPriority():获取线程优先级
public final void setPriority(int newPriority):更改线程的优先级
3、注意
优先级可以在一定的程度上,让线程获较多的执行机会
4、举例
MyThread t = new MyThread();
System.out.println(t.getPriority());
t.setPriority(Thread.MAX_PRIORITY);
7、暂停线程(Thread类中)
暂停当前正在执行的线程,让其他线程执行
2、成员方法
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
3、注意
是为了让线程更和谐一些的运行,但是你不要依赖这个方法保证,如果要真正的实现数据依次输出,请使用等待唤醒机制
8、加入线程(Thread类中)
如果调用一个线程的join方法,那么其他线程必须等待该线程执行完毕后才能执行
2、成员方法
public final void join():等待该线程终止
线程启动后调用该方法
9、守护线程(Thread类中)
1、成员方法
public final void setDaemon(boolean on):设置线程为守护线程,一旦前台(主线程),结束,守护线程就结束了
2、注意
main方法就本身是一个线程,我们在main方法里创建线程,并且设置线程为守护线程后,main方法结束后,守护线程就自动结束了