黑马程序员_多线程
来源:互联网 发布:ubuntu 14.04 iso 编辑:程序博客网 时间:2024/06/16 00:02
一、多线程
在说到多线程,我先简单说一下线程的概念,线程就是在进程中一个负责程序执行的控制单元,也叫执行路径。那么多线程就是一个进程中有多个执行路径称之为多线程。
为什么会有多线程,就是因为我们想让多个部分代码同时在运行,打个很简单的比方就是,射击游戏中子弹在运行着,人物也同时可以跑动,这就是线程在同时运行着他们,每一个线程都有自己的内容,这个内容就称为线程要执行的任务.
但是多线程有优点,也有弊端.它解决了多部分同时运行的问题.但是线程太多对导致效率降低.就比如电脑开了一个软件运行起来很流畅,我要是开十几个软件就卡的动不了.应用程序的执行由cpu做着快速切换完成的(这个切换是随机的),也就是说cpu其实就是一个"单线程",但是做着快速的切换去完成多个应用同时在运行,这个切换都是以毫秒计算,所以看不出来,但是应用程序多了,自然也就变慢了.哦了.
JVM启动时就会启动多个线程,只是有两个我们可以分析的出来.
1.执行main函数的线程;2.负责垃圾回收的线程.finalize()当垃圾回收器确定不存在对该对象更多引用时,由对象垃圾回收器调用此方法).
二、多线程详细图解
cpu的执行资格:可以被cpu处理,在处理队列中排队.
cpu的执行权:正在被cpu处理
创建线程的两种方式:1.定义类继承Thread类,覆盖run方法,直接创建Thread的子类对象创建线程,调用start方法开启;
JVM创建的主线程的任务都定义在主函数中,自定义的线程需要封装在run方法中,通过start开启进入run方法自定义的的线程.
2.定义类实现Runnable接口,覆盖接口中的run方法,将线程代码封装在run方法中,通过Thread类创建线程对象,并将Runnable接口子类对象作为Thread类的构造函数的参数传递.调用线程对象的start方法开启.
class Demo implements Runnable{ public void run() { show(); } public void show() { //方法内容... }}public static void main(String[] agrs){ Demo d = new Demo();Thread t = new Thread(d); t.start();}
第二种方式细节:Runnable他的出现仅仅是将线程的任务进行了对象封装,第一种方式继承Thread类,是说我们是线程的子类,任务只是我们对象中的一部分内容.而第二种方式是单独将线程的任务用对象封装起来,类型:Runnable
好处:(实现Runnable接口的好处也是区别)
1.将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象.
2.避免了java单继承的局限性.所以第二种方式较为常用.
三、线程安全问题
原因:既然是多线程,肯定涉及到1.多个线程在操作共享的数据;2.操作共享数据的线程代码有多余.
当一个线程在执行操作共享的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生.这时就出现了一个同步的概念.它是多个线程使用一个锁,当这个锁被一个线程拿着时,别的线程是无法继续执行的,解决了线程安全问题,但同时同步外的线程都会判断同步锁,效率也相对降低了.对于这个锁,可以是任意的对象.
//方式一:同步代码块synchronized(对象/相当与锁){ // 需要同步的代码;}//方式二:同步函数public synchronized void show(){ }
在这里同步函数的锁是this.同步代码快的锁是任意对象(开发建议)
在函数是静态的时候,new的对象不可能成为同步的锁,静态随类的加载而加载,在new对象之前,但是在字节码进入内存要先封装对象,这个对象就是当前class文件所属的对象.写法:当前类名.class,所以静态同步函数使用的锁是该函数所属字节码文件对象。
在这里说一下单例设计模式的多线程问题(面试题)
延迟加载单例设计模式(懒汉式)
class Single{ private static Single s = null; private Single(){} public static Single getInstance() { if(s==null)//提高效率,减少重复判断 { synchronized(Single.class) //同步安全 { if(s==null) {s=new Single()} } } }}根据程序分析,如果有多个线程进入,其中一个线程抢到Single.class的锁的情况下,这时s==null,会new一个对象,当下一个线程进来依然要判断是否为空,为了提高效率,我们将是否为空的判断在获得锁之前就先判断,这样就避免了锁的和对象是否为空的重复判断。
四、死锁
常见的情景之一:同步的嵌套
package com.qyc.dao;class Ticket implements Runnable {boolean flag = true;Ticket(boolean flag) {this.flag = flag;}/* * 分析: * 假设a先线程进入了if(),打开了locka锁,输出了...if....a * 这时b也进入了else,打开了lockb锁.输出了...if....b * 这时a想打开lockb锁,可是else中b持有着,所以a还打不开,就在这里等着b走完else将lockb锁释放 * 这时b需要打开locka锁,走出else才能释放lockb锁,但是a在if中持有着locka锁,b也打不开,也在这等待着a走完if将locka锁释放 * 最终都停在这里出不去,这就是死锁 * 如何解决: * 首先找到问题的所在,让他们共用的锁在释放后,才允许下一个线程持有这个锁,这个还是主要分析具体的情况,这代码就是一个死锁的代码示例 * */public void run() {if (flag) {synchronized (Mylock.locka) { System.out.println(Thread.currentThread().getName()+ "...if....a");synchronized (Mylock.lockb) {System.out.println(Thread.currentThread().getName()+ "...if....b");}}} else {synchronized (Mylock.lockb) {System.out.println(Thread.currentThread().getName()+ "...else....a");synchronized (Mylock.locka) {System.out.println(Thread.currentThread().getName()+ "...else....b");}}}}}class Mylock {public static final Object locka = new Object(); //locka用于线程a的锁public static final Object lockb = new Object(); //lockb用于线程b的锁}public class Test1 {public static void main(String[] args) throws InterruptedException {Ticket a = new Ticket(true); //让a走if语句Ticket b = new Ticket(false);//让b走else语句Thread t1 = new Thread(a);Thread t2 = new Thread(b);t1.start(); //开启线程t2.start();}}
五、JDK1.5版本后出现的新特性
Lock lock = new Reetrantlock();
原来同步代码块,对于锁的操作是隐式的,JDK1.5后将同步和锁封装了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
lock替换了synchronized方法和语句使用;Condtion替代了Object监视器方法使用。它对Object监视器对象,自己单独封装了一组对象,并可以将多个Condition对象使用一个锁
优点分析:以前一个锁上只有一组监视器,这个监视器既监视着生产者又监视着消费者,也就是有可能将生产者或消费者全部wait()或notify();具有着不确定性,而有了Condition监视器对象,可以来分组分别监视生产者和消费者.
package com.qyc.dao;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Product {private String water;private int count = 0;// 创建一个锁对象Lock lock = new ReentrantLock();// 创建两个监视器,一个监视生产者,一个监视消费者Condition con1 = lock.newCondition();Condition con2 = lock.newCondition();private boolean flag = false;public void production(String water) {lock.lock();try {while (flag) {try {con1.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}this.water = water;count++;System.out.println(Thread.currentThread().getName() + "生产第" + count+ "瓶" + water);flag = true;con2.signal();} finally {lock.unlock();}}public void consumption() {lock.lock();try {while (!flag) {try {con2.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println(Thread.currentThread().getName() + "消费第" + count+ "瓶" + water);flag = false;con1.signal();} finally {lock.unlock();}}}class Production implements Runnable {private Product p;Production(Product p) {this.p = p;}public void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}p.production("矿泉水");}}}class Consumption implements Runnable {private Product p;Consumption(Product p) {this.p = p;}public void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}p.consumption();}}}public class SynchronizedTest1 {public static void main(String[] args) {Product pro = new Product();Production p = new Production(pro);Consumption c = new Consumption(pro);Thread t0 = new Thread(p);Thread t1 = new Thread(p);Thread t2 = new Thread(c);Thread t3 = new Thread(c);t0.start();t1.start();t2.start();t3.start();}}
六、其他
停止线程(Interrupt()):对于停止有一种方法stop(),但是这种方法已经过时。还有一种方法是:run方法的结束.任务中都有循环,要控制循环就要结束循环.但是如果线程处于了冻结状态,无法读取标记,如何结束?可以使用 Interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格.强制会发生InterruptedException异常,记得处理。
守护线程(setDaermon):可以理解为后台线程,又称用户线程.注意:1.该方法必须在启动线程之前调用;2.当运行的线程都是守护线程,Java虚拟机是不管的,直接退出。
等待线程终止(join()方法):临时加入一个线程去运算时可使用join方法。
返回线程的字符串表示形式(toString()):包括线程名称、优先级(1~10)、线程组。更改线程优先级:setPriority(int newPriority)
线程组(ThreadGroup):可封装多个线程,对其操作.
暂停当前正在执行的线程(yield()):暂时释放一下执行权.
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- FWNX - power(double x,int n) - C++version
- 标准容器map的使用(创建优先级队列)
- Codeforces Round #221 (Div. 1)
- pathtext
- 路径加文字
- 黑马程序员_多线程
- VMware10.0+winDBG+win7 x64环境搭建
- VB实现不占用CPU的延时
- redis 主从配置实例、注意事项、及备份方式
- #ifdef使用
- AAM Alignment 算法的推导过程
- 16进制颜色(html颜色值)字符串转为UIColor
- 关于USB bulk设备中的CBW和CSW
- Perl 基础笔记: 使用 cpanm 安装 Perl 模块