进程与线程

来源:互联网 发布:h网络快还是e网络快 编辑:程序博客网 时间:2024/06/08 07:56
多线程:★★★★
进程 :正在进行中的程序。其实进程就是一个应用程序运行时的内存分配空间。
线程 :其实就是进程中一个程序执行控制单元, 一条执行路径。 进程负责的是应用程序的空间的标
示。线程负责的是应用程序的执行顺序。
23 / 67
一个进程至少有一个线程在运行, 当一个进程中出现多个线程时, 就称这个应用程序是多线程应用
程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。
jvm 在启动的时,首先有一个 主线程 ,负责程序的执行,调用的是 main 函数。主线程执行的代码
都在 main 方法中。
当产生垃圾时,收垃圾的动作, 是不需要主线程来完成, 因为这样,会出现主线程中的代码执行会
停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。
随机性的原理 :因为 cpu 的快速切换造成,哪个线程获取到了 cpu 的执行权,哪个线程就执行。
返回当前线程的名称: Thread.currentThread().getName()
线程的名称是由: Thread- 编号定义的。编号从 0 开始。
线程要运行的代码都统一存放在了 run 方法中。
线程要运行必须要通过类中指定的方法开启。 start 方法 。(启动后,就多了一条执行路径)
start 方法: 1)、启动了线程; 2)、让 jvm 调用了 run 方法。
创建线程的第一种方式:继承 Thread ,由子类复写 run 方法。
步骤:
1,定义类继承 Thread 类;
2,目的是复写 run 方法,将要让线程运行的代码都存储到 run 方法中;
3,通过创建 Thread 类的子类对象,创建线程对象;
4,调用线程的 start 方法,开启线程,并执行 run 方法。
线程状态:
被创建: start()
运行: 具备执行资格,同时具备执行权;
冻结: sleep(time),wait() —notify() 唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态: 线程具备 cpu 的执行资格,没有 cpu 的执行权;
消亡: stop()
创建线程的第二种方式:实现一个接口 Runnable。
步骤:
1,定义类实现 Runnable 接口。
24 / 67
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 接口将线程要执行的任务封装成了对象 。
-------------------------------------------------------
// 面试
new Thread(new Runnable(){ // 匿名
public void run(){
System.out.println("runnable run");
}
})
{
public void run(){
System.out.println("subthread run");
}
}.start(); // 结果: subthread run
25 / 67
---------------------------------------------------------
Try {
Thread.sleep(10);
}catch(InterruptedException e){} // 当刻意让线程稍微停一下,模拟 cpu  切换情况。
多线程安全问题的原因 :
通过图解:发现一个线程在执行多条语句时,并运算同一个数据时, 在执行过程中,其他线程
参与进来,并操作了这个数据。导致到了错误数据的产生。
涉及到两个因素:
1,多个线程在操作共享数据。
2,有多条语句对共享数据进行运算。
原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。
解决安全问题的原理 :
只要将操作共享数据的语句在某一时段让一个线程执行完, 在执行过程中, 其他线程不能进来执
行就可以解决这个问题。
如何进行多句操作共享数据代码的封装呢?
java 中提供了一个解决方式: 就是同步代码块。
格式:
synchronized( 对象) { // 任意对象都可以。这个对象就是锁。
需要被同步的代码;
}
---------------------------------------------------------------
同步:★★★★★ // 就是在操作共享数据代码时, 访问时只能让一个线程进去访
问,此线程执行完退出后,别的线程才能再对此共享数据代码进行访问。
好处: 解决了线程安全问题。 Synchronized
弊端 :相对降低性能,因为判断锁需要消耗资源,产生了死锁。
定义同步是有前提的 :
1,必须要有两个或者两个以上的线程,才需要同步。
2,多个线程必须保证使用的是同一个锁。
同步的第二种表现形式: // 对共享资源的方法定义同步
同步函数 :其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢? //synchronized(this) 用以定义需要进行同步的某一部分代码块
通过验证,函数都有自己所属的对象 this ,所以同步函数所使用的锁就是 this 锁。This. 方法名
当同步函数被 static 修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类, 这时有可能还没有该类产生的对象, 但是该类的字节码文件加载
进内存就已经被封装成了对象,这个对象就是 该类的字节码文件对象 。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
这个对象就是 类名 .class
同步代码块和同步函数的区别?
26 / 67
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是 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;
}
}
---------------------------------------------------------
同步死锁 :通常只要将同步进行嵌套, 就可以看到现象。 同步函数中有同步代码块,同步代码块中
还有同步函数。
线程间通信 :思路:多个线程在操作同一个资源,但是操作的动作却不一样。
1:将资源封装成对象。

2:将线程执行的任务 (任务其实就是 run 方法。 )也封装成对象。


等待唤醒机制: 涉及的方法:
wait: 将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线
程池中。
notify :唤醒线程池中某一个等待线程。
notifyAll: 唤醒的是线程池中的所有线程。
注意:
1:这些方法都需要定义在同步中 。
2:因为这些方法必须要标示所属的锁。
你要知道 A 锁上的线程被 wait 了, 那这个线程就相当于处于 A 锁的线程池中,只能 A 锁的
notify 唤醒。
3:这三个方法都定义在 Object 类中。为什么操作线程的方法定义在 Object 类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意
对象,那么能被任意对象调用的方法一定定义在 Object 类中。


wait 和 sleep 区别: 分析这两个方法:从执行权和锁上来分析:
wait :可以指定时间也可以不指定时间。不指定时间,只能由对应的 notify 或者 notifyAll
来唤醒。
sleep :必须指定时间,时间到自动从冻结状态转成运行状态 ( 临时阻塞状态 )。
wait :线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
线程的停止 :通过 stop 方法就可以停止线程。但是这个方式过时了。
停止线程:原理就是:让线程运行的代码结束,也就是结束 run 方法。
怎么结束 run 方法?一般 run 方法里肯定定义循环。所以只要结束循环即可。
第一种方式: 定义循环的结束标记。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过 Thread 类中
的 interrupt 方法,将其冻结状态强制清除。 让线程恢复具备执行资格的状态, 让线程可以读到标
记,并结束。


---------< java.lang.Thread >----------
interrupt() :中断线程。
setPriority(int newPriority) :更改线程的优先级。
getPriority() :返回线程的优先级。
toString() :返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread.yield() :暂停当前正在执行的线程对象,并执行其他线程。
setDaemon(true) :将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时, Java 虚拟机退出。该方法必须在启动线程前调用。
join :临时加入一个线程的时候可以使用 join 方法。
当 A线程执行到了 B线程的 join 方式。 A线程处于冻结状态,释放了执行权, B开始执行。 A
什么时候执行呢?只有当 B线程运行结束后, A才从冻结状态恢复运行状态执行。


同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是 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;
}
}
---------------------------------------------------------
同步死锁 :通常只要将同步进行嵌套, 就可以看到现象。 同步函数中有同步代码块,同步代码块中
还有同步函数。


等待唤醒机制: 涉及的方法:
wait: 将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线
程池中。
notify :唤醒线程池中某一个等待线程。
notifyAll: 唤醒的是线程池中的所有线程。
注意:
1:这些方法都需要定义在同步中 。
2:因为这些方法必须要标示所属的锁。
你要知道 A 锁上的线程被 wait 了, 那这个线程就相当于处于 A 锁的线程池中,只能 A 锁的
notify 唤醒。
3:这三个方法都定义在 Object 类中。为什么操作线程的方法定义在 Object 类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意
对象,那么能被任意对象调用的方法一定定义在 Object 类中。


wait 和 sleep 区别: 分析这两个方法:从执行权和锁上来分析:
wait :可以指定时间也可以不指定时间。不指定时间,只能由对应的 notify 或者 notifyAll
来唤醒。
sleep :必须指定时间,时间到自动从冻结状态转成运行状态 ( 临时阻塞状态 )。
wait :线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
线程的停止 :通过 stop 方法就可以停止线程。但是这个方式过时了。
停止线程:原理就是:让线程运行的代码结束,也就是结束 run 方法。
怎么结束 run 方法?一般 run 方法里肯定定义循环。所以只要结束循环即可。
第一种方式: 定义循环的结束标记。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过 Thread 类中
的 interrupt 方法,将其冻结状态强制清除。 让线程恢复具备执行资格的状态, 让线程可以读到标
记,并结束。


---------< java.lang.Thread >----------

interrupt() :中断线程。
setPriority(int newPriority) :更改线程的优先级。
getPriority() :返回线程的优先级。
toString() :返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread.yield() :暂停当前正在执行的线程对象,并执行其他线程。
setDaemon(true) :将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时, Java 虚拟机退出。该方法必须在启动线程前调用。
join :临时加入一个线程的时候可以使用 join 方法。
当 A线程执行到了 B线程的 join 方式。 A线程处于冻结状态,释放了执行权, B开始执行。 A
什么时候执行呢?只有当 B线程运行结束后, A才从冻结状态恢复运行状态执行。


LOCK的出现替代了同步: lock.lock(); ,,, lock.unlock();
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();
}
}
}

0 0
原创粉丝点击