java线程(⼀)线程的基本概念以及创建的5种⽅法
在学习线程之前,就要知道什么是线程。
任务调度:
⼤部分操作系统都是采⽤时间⽚轮转的抢断式调度,简单来说就是让任务执⾏⼀⼩段时间后强制暂停去执⾏另⼀个任务,任务执⾏的⼀⼩段时间就是时间⽚。由于cpu的执⾏效率⾮常⾼,时间⽚⾮常短,各个任务之间的切换⾮常快。让⼈感觉好像这些任务在同时执⾏。
举个例⼦:你去和国⼿去下围棋,⽽国⼿同时和包括你在内的100个⼈同时下棋,你下⼀步棋需要经过⼀定的思索,⽽国⼿和你们下棋⾮常的轻松⼏乎不需要思考。你特别认真的和国⼿下棋,以为国⼿只和你⼀个⼈下棋,但其实国⼿同时在和你们100个⼈下棋。国⼿相当于cpu,⽽你们相当于⼀个个任务,国⼿和你下棋的⼀瞬间就是时间⽚。
进程:
进程是⼀个具有⼀定独⽴功能的程序在⼀个数据集上的⼀次动态的执⾏过程。有⾃⼰的⽣命周期,产⽣和消亡。每个进程都有⾃⼰的内存空间。
线程:
进程中的⼀个控制单元,负责当前进程中的程序执⾏,⼀个进程⾄少有⼀个线程,⼀个进程可以运⾏多个线程,多个线程可以共享数据。
进程和线程的区别:
根本区别:进程是操作系统资源分配的基本单元,⽽线程是处理器任务调度的和执⾏的基本单位。
资源开销:每个进程都有⾃⼰独⽴的代码和空间(程序上下⽂),程序之间的切换会有较⼤的开销;线程可以看作轻量级的进程,同⼀类线程共享代码和数据空间,每个线程都有⾃⼰独⽴的运⾏栈和程序计数器(PC),线程之间切换的开销⼩。
包含关系:如果⼀个进程内有多个线程,则执⾏的过程不是⼀条线的,⽽是多条线(多个线程),共同完成;线程是进程的⼀部分,可以把线程看作是轻量级的进程。
内存分配:同⼀进程的线程共享本进程的地址空间和资源,⽽进程之间的地址空间和资源是相互独⽴的。
什么是死锁?
所谓死锁,是指多个进程在运⾏过程中因争夺资源⽽造成的⼀种僵局,当进程处于这种僵持状态时,若⽆外⼒作⽤,它们都将⽆法再向前推进。
死锁产⽣的4个必要条件
1、互斥: 某种资源⼀次只允许⼀个进程访问,即该资源⼀旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待: ⼀个进程本⾝占有资源(⼀种或多种),同时还有资源未得到满⾜,正在等待其他进程释放该资源。
3、不可抢占: 别⼈已经占有了某项资源,你不能因为⾃⼰也需要该资源,就去把别⼈的资源抢过来。
4、循环等待: 存在⼀个进程链,使得每个进程都占有下⼀个进程所需的⾄少⼀种资源。
当以上四个条件均满⾜,必然会造成死锁,发⽣死锁的进程⽆法进⾏下去,它们所持有的资源也⽆法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使⽤性能的。那么,解决死锁问题就是相当有必要的了。
避免死锁的⽅法
1、死锁预防 ----- 确保系统永远不会进⼊死锁状态
产⽣死锁需要四个条件,那么,只要这四个条件中⾄少有⼀个条件得不到满⾜,就不可能发⽣死锁了。由于互斥条件是⾮共享资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产⽣死锁的其他三个条件。
a、破坏“占有且等待”条件
⽅法1:所有的进程在开始运⾏之前,必须⼀次性地申请其在整个运⾏过程中所需要的全部资源。
优点:简单易实施且安全。
缺点:因为某项资源不满⾜,进程⽆法启动,⽽其他已经满⾜了的资源也不会得到利⽤,严重降低了资源的利⽤率,造成资源浪费。 使进程经常发⽣饥饿现象。
⽅法2:该⽅法是对第⼀种⽅法的改进,允许进程只获得运⾏初期需要的资源,便开始运⾏,在运⾏过程中逐步释放掉分配到的已经使⽤完毕的资源,然后再去请求新的资源。这样的话,资源的利⽤率会得到提⾼,也会减少进程的饥饿问题。
b、破坏“不可抢占”条件
当⼀个已经持有了⼀些资源的进程在提出新的资源请求没有得到满⾜时,它必须释放已经保持的所有
资源,待以后需要使⽤的时候再重新申请。这就意味着进程已占有的资源会被短暂地释放或者说是被抢占了。
该种⽅法实现起来⽐较复杂,且代价也⽐较⼤。释放已经保持的资源很有可能会导致进程之前的⼯作实效等,反复的申请和释放资源会导致进程的执⾏被⽆限的推迟,这不仅会延长进程的周转周期,还会影响系统的吞吐量。
c、破坏“循环等待”条件
可以通过定义资源类型的线性顺序来预防,可将每个资源编号,当⼀个进程占有编号为i的资源时,那么它下⼀次申请资源只能申请编号⼤于i的资源。
以上是有关线程中的⼀些概念和解释,下⾯让我们⼀起来看看创建线程的五种⽅法
1.继承Thread来创建线程(重写的是⽗类的Thread的run())
A j=new A();
B w=new B();
//调⽤线程
j.start();
w.start();
}
}
class A extends Thread{//继承Thread类
public void run(){
//循环输出
for (int i=0;i<10;i++){
try {
//睡眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你是谁");
}
}
}
class B extends Thread{//继承Thread类
public void run(){
for (int i=0;i<10;i++){
try {
/
/睡眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你猜猜");
}
}
}
运⾏结果:
运⾏结果为交替进⾏输出,你是谁,你猜猜,两秒后继续输出你是谁,你猜猜。由于设定了睡眠两秒后执⾏所以执⾏输出你是谁后,两秒后才能继续执⾏,⽽这时输出你猜猜执⾏。
设定这个睡眠时间的作⽤是为了防⽌某⼀个线程⼀直执⾏。设定睡眠时间来让这俩个线程可以交替进⾏。
2.实现Runnable接⼝,重写run()
Thread tt=new Thread(t);//将任务放到线程的构造器 tt.start();//启动线程
}
}
class T implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(".......");
}
}
一个线程可以包含多个进程
}
输出结果:
每隔三秒输出⼀个。。。。。。循环10次
3.匿名内部类
public class Demo03 {
public static void main(String[] args) {
//相当于继承了Thread类,作为⼦类重写run()实现 new Thread(){
public void run(){
System.out.println("匿名内部类创建⽅式⼀"); }
}.start();
//相当于实现了Runnable作为匿名内部类
new Thread(new Runnable(){
public void run(){
System.out.println("匿名内部类创建⽅法⼆");
}
}).start();
}
}
运⾏结果:
4.实现Callable(带返回值的线程)
public class Demo01 {
public static void main(String[] args) {
MyCallable callable=new MyCallable();
FutureTask<String> task=new FutureTask(callable);
task.run();
try {
String ();
System.out.println(o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<String>{
public String call()throws Exception{
Thread.sleep(5000);
return "abc";
}
}
运⾏结果:五秒后输出abc
5.线程池
FixedThreadPool固定⼤⼩的线程池
public class Demo04 {
public static void main(String[] args) {
/
/FixedThreadPool固定⼤⼩的线程池
//创建带有5个线程的线程池
ExecutorService ex= wFixedThreadPool(5);
for (int i=0;i<5;i++){
ex.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); }
});
}
//销毁线程池
ex.shutdown();
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论