线程与进程概述
在学习线程之前要先知道什么是进程,进程就是正在运行的程序,它是系统资源调度的独立单位,并且一个进程可以执行多个任务,而线程就是程序执行的任务,它是程序使用CPU的基本单位,因此也可以说线程是依赖于进程的
进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:(可分为单线程与多线程)
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
单线程:
单线程的计算机一次只能做一件事,做的事情单一,也就是程序中只有一条执行路径,效率比较低
多线程:
多线程的计算机可以同时做不同的事情,比如一边听歌,一边打游戏,这两件事虽然感觉起来是在同时发生的事,其实不过是CPU在做着程序之间的高速切换。多线程的存在不是为了提高程序的运行速度,而是为了提高应用程序的使用率,也可以说程序的执行其实都是在抢CPU的资源,也就是抢CPU的执行权,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权,但这一过程是随机的,不知道哪一个线程会在哪一个时刻占到这个资源,所以线程的执行有随机性。
线程调度:
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的是抢占式调度
CPU使用抢占式调度模式在多个线程之间进行着高速的切换,对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程建切换速度相对于我们的感觉要快,看上去就是在同一个时刻运行,其实,多线程并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高
线程的创建以及启用
线程可以完成一定任务,可以和其它线程共享父进程的共享变量和部分环境,相互协作来完成任务。
线程是独立运行的,其不知道进程中是否还有其他线程存在。
线程的执行是抢占式的,也就是说,当前执行的线程随时可能被挂起,以便运行另一个线程。
一个线程可以创建或撤销另一个线程,一个进程中的多个线程可以并发执行。
线程的创建和启用:
java使用Thread类代表线程,所有的线程对象都必须是Thread或者其子类的实例,每个线程的作用是完成一定任务,实际上是就是执行一段程序流(一段顺序执行的代码)
方法一、继承Thread类创建线类
1:定义Thread类的子类 并重写该类的Run方法 该run方法的方法体就代表了线程需要完成的任务
如下:
public class MyThread extends Thread{
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run(){
//这里的代码 就是一条新的执行路径
//这个执行路径的触发1方式,不是调用run方法,而是通过Thread对象的start()来触发
for (int i=0;i<5;i++){
System.out.println("白茶清欢无别事"+i);
}
}
2:创建Thread类的实例,即创建了线程对象
3:调用线程的start方法来启动线程
举例说明
public class Demo1 {
public static void main(String[] args) {
//创建Thread对象
MyThread thread = new MyThread();
//调用线程的start方法来启动线程
thread.start();
//自定义输入
for (int i=0;i<5;i++){
System.out.println("我在等风也等你"+i);
}
}
}
运行效果如图:
使用Runnable接口创建线程类
1:定义Runnable接口的实现类,并重写它的Run方法,run方法同样是该线程的执行体!
定义Runnable接口的实现类:
public class MyRunnable implements Runnable {
@Override
//线程的任务
public void run() {
for (int i=0;i<5;i++){
System.out.println("窗前明月光"+i);
}
}
}
2:创建Runnable实现类的实例,并将此实例作为Thread的target创建一个Thread对象,该Thread对象才是真正的线程对象!
3:调用start方法启动该线程
/**
* 多线程技术
* 实现Runnable 与 继承Thread相比有如下优势
* 1、通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行
* 2、可以避免单继承带来的局限性
* 3、任务与线程是分离的,提高了程序的健壮性
* 4、后续学习的线程池技术,接受Runnable类型,不接收Thread类型
*/
public class Demo2 {
public static void main(String[] args) {
//实现Runnable
//1、 创建一个任务对象
MyRunnable r = new MyRunnable();
//2、 创建一个线程,并为其分配一个任务
Thread t = new Thread(r);
//3、 执行这个线程
t.start();
for (int i=0;i<5;i++){
System.out.println("举头望明月"+i);
}
}
}
运行效果:
方法三:匿名内部类实现线程
public class Demo3 {
public static void main(String[] args) {
//匿名内部类实现线程
new Thread(){
@Override
public void run(){
for (int i=0;i<5;i++){
System.out.println("想太多了"+i);
}
}
}.start();
for (int i=0;i<5;i++){
System.out.println("加油吧,少年!"+i);
}
}
}
运行效果如图:
多线程的常用方法
setName(String name) 将此线程的名称更改为等于参数 name
getName() 返回此线程的名称
currentThread() 返回对当前正在执行的线程对象的引用
事例如下:
public class Demo4 {
public static void main(String[] args) {
//如何获取线程的名字
new Thread(new MyRunnable()).start();
//在new的时候传入线程名称,或者用setName方法
new Thread(new MyRunnable(),"锄禾日当午").start();
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run(){
//获取当前正在运行的线程的名称
System.out.println(Thread.currentThread().getName());
}
}
}
运行效果:
sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
sleep(long millis, int nanos) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。
事例如下:
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
//线程休眠 sleep
for (int i=0;i<8;i++){
System.out.println(i);
//抛出异常
//sleep两种方法调用
//1、导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性
//2、导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性
Thread.sleep(1000);
}
}
}
interrupt() 中断此线程。
interrupted() 测试当前线程是否已被中断
事例如下:
/**
* 线程的中断
*/
public class Demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRunnable(),"哈哈哈");
t1.start();
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
Thread.sleep(1000);
}
//中断此线程 t1
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("发现中断标记!");
//发现中断标记之后,若想要线程中断,可以用return,返回run方法
return;
}
}
}
}
}
运行效果如下:
setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程
示例如下:
/**
* 守护线程 :用于守护用户线程,当最后一个用户线程结束时,所有守护线程自动死亡
* 用户线程:当一个进程不包含任何一个存活的用户线程时,进行结束
*
*
*/
public class Demo7 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Demo6.MyRunnable(),"哈哈哈");
//设置t1为守护线程,true为守护线程
t1.setDaemon(true);
t1.start();
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
Thread.sleep(1000);
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
/*System.out.println("发现中断标记!");
//发现中断标记之后,若想要线程中断,可以用return,返回run方法
return;*/
}
}
}
}
}
isAlive()方法的功能是判断当前的线程是否处于活动状态
示例如下:
class Mythread extends Thread{
@Override
public void run() {
System.out.println("run =="+this.isAlive());
}
}
public class Test01 {
public static void main(String[] args) {
Mythread thread=new Mythread();
System.out.println("begin =="+thread.isAlive());//①
thread.start();//②
System.out.println("end =="+thread.isAlive());//③
}
}
结果:
begin ==false
end ==true
run ==true
方法isAlive()的作用是测试线程是否处于活动状态。那么什么情况下是活动状态呢?活动状态就是线程已经启动且尚未停止。线程处于正在运行或准备开始运行的状态,就认为线程是存活的