进程和线程
“进程是资源分配的最小单位,线程是CPU调度的最小单位”。
进程:在进程创建时,需要从系统那里申请一定的独立空间进行程序的运行。进程之间的空间是相互独立的,无法互相访问。
线程:准确的说程序执行流的最小单元,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
包package
建立包
格式:Package pack // 建立对应的包目录
编译时指定要存放class的位置。
注意:
1、 一个类在继承其他包中的类时,需要包名.类名
2、 要被其他包中的类想要被继承,需要申明为public或protected类型
可访问状态:
状态 | Public | Protected | Default | Private |
同一包中 | OK | OK | OK | NO |
同一类中 | OK | OK | OK | OK |
子类间 | OK | OK | OK | NO |
不同包中 | OK | OK | NO | NO |
导出包:
格式:import 包名.*;
多线程:
进程:正在执行中的程序,每个程序都有一个执行顺序。即执行路径或者叫一个控制单元。(目的:封装控制单元,为整个程序开辟需要的内存)
线程:进程中一个独立的控制单元,线程控制进程的执行,并且一个进程中至少有一个线程。
注:java中至少有一个线程负责java程序执行,而这个线程运行的代码存在于main中,该线程为主线程。
创造线程的方法一:继承方法
1、 定义类继承Thread。(Thread用于描述线程,该类定义了一个功能,用于存储要运行的方法,即run()方法)
2、 复写Thread中run方法。(用于存储线程要运行的代码,封装代码)
3、 调用线程start方法;(start作用:(1)启动线程;(2)调用run方法)
注意:start相当于发令枪,一个线程仅有一个发令枪。
线程都有自己的特有名称,用getName获得。
Static Thread currentThread():返回当前运行的线程对象
创造线程的方法二:实现方法
1、 定义类实现Runable接口
2、 覆盖Runnable接口中的run方法(存放要运行的代码)
3、 通过Thread建立线程对象。
Thick t = new Ticket();
4、 将Runnable接口子对象作为实参,传递给Thread类构造函数
Thread t1 = new Thread(t);
t1.start();
(自定义的run方法为Runnable接口的子类对象,所以要让线程去执行指定对象run方法,需要明确所属的对象)
5、 调用Thread类的start方法,开启线程,并调用Runnable接口子类的run方法
注:t1其用到的构造函数:
Thread(
Runnable target)
分配新的 Thread
对象。
实现方法VS继承方法
实现方式:避免了单继承的局限性(建议使用)
相同点:都需要复写run方法。
区别:
1、 继承:线程代码存放在thread子类run方法中
2、 实现:线程代码存放在接口子类的run方法中
注意:
1、当使用继承的时候,主要是为了不必重新开发,并且在不必了解实现细节的情况下拥有了父类我所需要的特征。它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,
2、java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了,在其他的方面,两者之间并没什么太大的区别。
3、implement Runnable是面向接口,扩展性等方面比extends Thread好。
4、使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。
多线安全问题:
当多条线程语句在操作同一共享数据时,一线程只得到了运行资格,但还未提取数据被另外一个线程打断并取走数据,导致共享数据错误。
在第14行,t1刚执行到此被t2打断,t2执行到此被t3打断。因为此时大家都判定tick>0所以后续会继续执行。结果就是,出现负数。这就产生了多线程错误。
解决方法:对多条操作共享的舞剧让一个线程完全执行玩(其他线程不能打扰)。即使用同步代码块。
同步代码块:
语法:
Synchronized(对象)// 这里的对象为持锁的对象,明确谁要对接下来的数据处理
{
需要被同步的代码;
}
对象如同锁,持锁的线程可以在同步代码快中执行,没有锁额对象的线程,即使获得CPU执行权也进不去。
Eg:火车上的卫生间标志。
锁的概念:分为对象锁和类锁
java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的
注意:放进去的代码部分应该为所共同操作的资源部分。
同步的前提:
1、 必须有两个或两个以上的线程
2、 必须是多个线程执行同一个锁
目的:保证资源仅被一个线程执行
好处VS弊端
好处:解决了多线程安全问题
弊端:增加了资源的消耗(CPU资源)
多线程需要注意的点:
1、 明确哪些代码是多线程运行代码
2、 明确共享数据
3、 明确多线程运行代码中哪些语句操作共享数据
同步函数
将Synchronized放在修饰位置上即同步函数;
问题:同步函数的锁是什么?
答:函数要被对象调用,那么函数有一个所示对象的引用,就是this。因此锁为this。
(若同步函数被static修饰,同步函数的锁不为this。因为其中不可定义this。但在类创建时,其产生了该类的字节码文件对象,即:类名.class 该对象的类型为本类的calss(字节码对象))
再来看懒汉单例模式:
说明:懒汉模式与恶汉模式最大的区别在于其创建对象的延时性。在多线程操作过程中,容易引起线程错误。为了避免该错误便引入锁。但因为每次创建都要先判锁,导致效率不高,因此在之前加入S判断,减少锁的判断次数,从而减少消耗。其锁为Single的class文件
死锁:
Eg:一人一支筷子,相互请求吃饭。
原因:同步中嵌套同步,锁却不同。
程序例子
这里的锁为了好区分,定义了MyLock对象。在完美情况下,t1拿到执行权在21行等待t2。这时t2获取执行权执行接下来的代码。如果运气差的话,t1拿到执行权,去等待锁b,而这时t2也刚刚建立,执行到32行等待锁a,于是两个就互相等,形成死锁。归结到底是执行权的问题
ReetrantLock; await; singal “VS” Synchronized ,wait, notigy
1、 Lock lock = new ReetrantantLock(); // 产生一个可冲入的互斥锁
…….lock.lock()
lock.unlock()
|
2、 Condition con =lock.newCondition() ;// 将Object监视器方法分成不同对象,一边同任意lock实现结合,提供多个对象等待。其返回值为绑定该lock的心Condition实例。
Con.await()= this.wait();
Con.singal= this.notify ;
多线程间通信
在对线程通信过程中,每个对象都要用到同一个资源库,而Synchrinizied则是为资源库上锁,防止一个资源在同一时间被多个用户使用。资源库需要自带标志,以表明自己此时是否备用。而在唤醒线程之前需要循环查看此标志。
关键字:
1、 .join():对调用他的线程,会一直执行到该线程死为止。
2、 setDaemon():将线程设置为守护线程,当前台线程结束后,该线程自动结束。
3、 wait():等待,有执行资格但是没有执行权,一直要到被唤醒。(等待中的线程都被放在线程池中)
4、 interrupt():当进入冻结状态时,将其强制进行恢复为运行。
5、 notify() : 唤醒线程。每次唤醒只唤醒线程池中队列前的线程。
wait() , notify() ,nitifyAll()
都使用同步类中,因为要对持有监视器的锁的线程操作,所以要用在同步中,因为只有同步才具有锁。
notify():唤醒一个;唤醒队列前的线程。
notifyAll():唤醒全部等待线程;
问:为什么定义在Object类中呢?
答:因为所有对象都可以成为锁。
JDK1.5新特性:
其提供了多线程升级解决方案。
在资源中,将同步Synchronized替换成显示的Lock操作。将Object中的wait,notify替换了Condition对象。Condition对象可以在同一个对象中存在多个。以表示对不同对象在使用资源时进行唤醒或等待。
Synchronized 和Lock实例函数
升级之前:
升级之后:
在synchronized方法中:
他操作的是Object对象,相对应的函数为(wait,notify,notifyAll);
在他当中锁对象只能有一个;this.class;
在Lock创建的ReentrantLock对象中:
他操作的是condition对象,相应的函数为(await, signal,signalAll)
他当中锁对象能有很多个:
Synchronized “VS” ReentrantLock
1. 如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
2. synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3. 在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
线程停止的方法:让run方法结束
一般多线程,运行代码通常是循环结构,只要控制住循环就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于冻结状态,就不会结束。当没有方式让线程回复到运行状态时,就需要对冻结进行清除。强制让线程回复到运行中来。这样就可以操作标记让线程结束。(Thread 提供了interrupt方法。)
当t1 interrrupt之后他会跑到自己的run函数中去找异常处理语句。
后台线程(守护线程) VS 前台线程
开启后和前台线程共同抢CPU,但当前台线程结束之后,后台线程自动结束。
Eg:在制造商品和出售商品的例子中,出售商品应该设为制造商品的守护线程,因为没有制造商品就没有出售商品。
线程中一些操作:
1、 设置优先级:setPriority():设置后,让能抢到CPU的可能性更高(总共10级,默认为5,越高则获取cpu执行的几率越大)。
t1.setpriority(Thread_MAX_PRIORITY);
| setPriority |
2、 暂停正在执行的形成:.yield()。目的为,平均线程的执行次数,你一下我一下。
Thread.yield();强制释放其执行权
| yield |
3、 让某线程执行到死才释放cpu使用权。t1.join();
| join |
常用的线程操作:
Thread类:
1. 获取有名字的线程对象
Thread(
String name)
分配新的 Thread
对象。
如果没有给名字,则其默认的名字为
2. 获取线程名称:
getName() |
3. 获取当前正在执行的线程
static Thread | currentThread() |
4. 将该线程进入冻结状态
static void | sleep(long millis) |
注意:
他有抛出异常,需要try catch进行处理。