黑马程序员_多线程技术
来源:互联网 发布:开淘宝店服装去哪进货 编辑:程序博客网 时间:2024/05/18 13:30
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
名词解析:
进程:一个程序在内存中的所处空间。进程只分配应用程序的内存空间,并不执行应用程序。
线程:就是进程中一个负责程序执行中的控制单元(执行路径)
垃圾回收器:垃圾回收线程
当垃圾回收时会执行object的finalize方法,我们可以用system.gc()来通知垃圾回收线程要开始回收垃圾了。
并发:就是”同时”的意思
CPU执行资格:当某线程start()开启之后就有了CPU执行资格,当线程sleep()或wait()时也就是处于冻结状态时就不具备CPU执行资格了。
CPU执行权:当CPU正在处理某线程时,某线程就具备了CPU执行权。
随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。
返回当前线程的名称:Thread.currentThread().getName()
线程的名称是由:Thread-编号定义的。编号从0开始。
线程生命周期:
线程状态:
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()
创建线程:
法一:创建一个类继承Thread类
1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
法二:实现Runnable接口,然后new一个实现该接口的类,在把该实例作为new Thread的构造参数
1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。
Ticket t = new Ticket();
/*
直接创建Ticket对象,并不是创建线程对象。
因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。
所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。
*/
Thread t1 = new Thread(t); //创建线程。
/*
只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联
为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。
*/
t1.start();
实现Runnable接口的好处:
1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。
线程方法:
开启多个线程后区分现在正在运行的线程是那个?
通过Thread.getName获取线程的名称
通过Thread.currentThread()获取当前运行的线程名
使线程冻结的方法
sleep(time)就是把该线程冻结time时间,time时间过后自动唤醒,释放执行权,释放锁。
wait()释放执行权,不释放锁。
notify()唤醒线程。
线程安全问题:
多线程安全问题的原因:
通过图解:发现一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其他线程参与进来,并操作了这个数据。导致到了错误数据的产生。
涉及到两个因素:
1,多个线程在操作共享数据。
2,有多条语句对共享数据进行运算。
原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。
解决安全问题的原理:
只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决这个问题。
如何进行多句操作共享数据代码的封装呢?
java中提供了一个解决方式:就是同步代码块。
格式:
synchronized(对象) { // 任意对象都可以。这个对象就是锁。
需要被同步的代码;
}
同步:
synchronized(对象){
要同步的代码块。
}
好处:解决了线程安全问题。
弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁。
定义同步是有前提的:
1,必须要有两个或者两个以上的线程,才需要同步。
2,多个线程必须保证使用的是同一个锁。
同步的第二种表现形式:
同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢?
通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁。
当同步函数被static修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。这个对象就是 类名.class
同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
请写一个延迟加载的单例模式?写懒汉式;当出现多线程访问时怎么解决?加同步,解决安全问题;效率高吗?不高;怎样解决?通过双重判断的形式解决。
//懒汉式:延迟加载方式。
当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。所以容易出现线程安全问题。为了解决,加入同步机制,解决安全问题。但是却带来了效率降低。
为了效率问题,通过双重判断的形式解决。
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(); } } return s; }}
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class获取。
一般函数可以用synchronized关键字修饰,函数中持有的参数是this,可以如下证明:
public synchronized void show(){}
类似于
public void show(){
synchronized(this){}
}
同步代码块的意思就是将多条操作共享数据的线程代码块封装起来,当有线程在执行这些代码块的时并且没有执行完,其他线程始终进不来。
synchronized解决问题的原理:
举个生活中的例子:synchronized关键字后面的参数相当于是一把钥匙,代码块里面的内容想当于房间内的东西。现在有4个对象,当Thread-0执行到synchronized这个代码块时,看到门上面插着钥匙,所以就那着钥匙进去了,进去后CPU切换到Thread-1,Thread-1执行到synchronized时看到门上面没有钥匙,所以进不去,不能动房子里面的东西。
死锁:常见情景之一:同步的嵌套
线程之间的通信:
多个线程在处理同一资源,但任务却不同
等待唤醒机制涉及到的方法:1、wait、notify、notifyAll
1、wait():让线程处于冻结状态,被wait的线程会被存储到线程池
2、notify():唤醒线程池中的一个线程(随机)
3、notifyAll():唤醒线程池中的所有线程
线程经典例子:生产者与消费者这些方法都必须定义在同步中,因为这些方法是操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程。一个锁对应着一个线程池。
为什么这些操作线程的方法定义在了Object类中?因为这是监视器的方法,监视器就是锁,而锁可以为任意对象。
一个生产者和一个消费者:
比较简单,对同步和wait()、notify()方法的运用
多个生产者和多个消费者:
会出现产生多个产品,但没有消费与之对应的产品个数,比如生产编号为1和2号的产品,但只消费了编号为1的产品。还有一种情况是只生产了编号为1号的产品,但只消费了编号为1和2号的2个产品,这样就不对应。
1、解决方法:在判断是什么时候生产产品的时候判断标记加入循环。
解决效果:解决了上面出现的问题,但出现了一个新的问题,死锁,因为所有的线程都被处于wait状态。
2、解决方法:把notify该为notifyAll()
Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口。
解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。
到了后期版本,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。
在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。
所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。
在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。
而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。
< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();
--------------------------------------------------------
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }停止线程
1.stop方法(不占成使用)
2.run方法结束(通过控制循环,定义标记结束线程。当线程处于冻结状态就无法读取标记就不能结束)。
3.Interrupt方法可以将动态线程强制恢复到运行状态中来,让线程具备CPU执行资格,但是强制中断会发生异常。
守护线程
setDaemon方法是守护线程,该线程被守护后就会在虚拟机结束后继续执行。
Thread.yield()释放执行权。
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
- 黑马程序员_多线程技术
- 黑马程序员_多线程技术
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- 黑马程序员_多线程
- storm进程正常运行一段时间shut down,运维方式
- android中常用的弹出提示框
- android调用音乐播放器,三种方
- 端口占用查询
- phpcms v9 做的网站搬家之后路径不变的做法
- 黑马程序员_多线程技术
- 浅谈IM软件的“假在线’
- 最最爱的小麦
- 解析SS、SP、BP寄存器
- 从本地选取相册和相机拍照
- the compact org-mode guide 第四章
- 土地利用规划环境评价的意义
- boost::serialization 多态(polymorphic)序列化技巧及分析
- poj-1035