多线程-创建,API操作,状态转换

多线程

1,什么是进程,什么是线程

  • 1,进程:

    • 系统中运行的一个程序,程序一旦运行起来就是进程
  • 2,线程:

    • 它被包含在进程之中,是进程中的实际运行单位,一条线程指的是进程中的一个单一顺序的控制流。
    • 一个进程中可以并发多个线程,每一条线程并行执行不同的任务。

并发和并行

  • 并发:CPU一核,多线程操作同一个资源
  • 并行:CPU多核,多个线程可以同时执行。

程序的运行原理

  • 分时调度:所有线程轮流使用cpu的使用权
  • 抢占式调度:优先让优先级最好的线程使用cpu,如果线程的优先级相同,那么cpu会随机挑选一个执行,Java使用的为抢占式调度。不提高程序的运行速度,但能够提高程序的运行效率

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

守护程序:守护线程,是指在程序运行时 在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。通俗点讲,任何一个守护线程都是整个JVM中所有非守护线程的"保姆"。

创建线程的实现方式。

继承Thread类

  • 将一个类声明为Thread的子类,这个子类重写了Thread的run()方法,然后可以分配启动子类的实例。
  • start()方法是启动多线程,需要用继承Thread类的类对象来启动它,run()方法里是多线程的执行体。
public class DemoSon1 extends Thread{
    public static void main(String[] args) throws Exception{
        //new DemoSon1().run();
        //currentThread()返回当前执行的对象名
        System.out.println(new Thread().currentThread().getName());
        DemoSon1 demoSon1 = new DemoSon1();
        //start()在主方法启动时调用重写的run方法
        demoSon1.start();

        for(int i =0 ; i<10;i++){
            System.out.println("主方法执行了"+i);
            Thread.sleep(1000);
        }
    }
    public void run() {
        //返回当前线程的名字
        System.out.println("当前线程的名字"+this.getName());
        for(int i =0 ; i<10;i++){
            System.out.println("run方法执行了"+i);
            try {
                //线程休眠
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


//运行结果:
main
主方法执行了0
当前线程的名字Thread-1
run方法执行了0
run方法执行了1
主方法执行了1
run方法执行了2
主方法执行了2
run方法执行了3
主方法执行了3
主方法执行了4
run方法执行了4
主方法执行了5
run方法执行了5
run方法执行了6
主方法执行了6
主方法执行了7
run方法执行了7
run方法执行了8
主方法执行了8
主方法执行了9
run方法执行了9

实现runnable接口

  • 声明一个实现Runnable接口的类,这个类实现了run()方法,可以分配类的实例,在创建Thread时作为参考,并启动
  • 重写run()方法,启动时需要创建Thread类对象,同时创建时还要将实现Runnable接口的类对象放进去,再使用Thread类对象调用start()方法启动多线程
public class DemeSon implements Runnable{
    public static void main(String[] args)  {

        DemeSon demeSon = new DemeSon();
        Thread thread = new Thread(demeSon);
        thread.start();
        for (int i=0;i<10;i++){
            System.out.println("主方法线程"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
    @Override
    public void run() {
        //返回当前线程的名字
        System.out.println("当前线程的名字"+new Thread().getName());
        for(int i =0 ; i<10;i++){
            System.out.println("run方法执行了"+i);

                //线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}


//执行结果
主方法线程0
当前线程的名字Thread-1
run方法执行了0
主方法线程1
run方法执行了1
run方法执行了2
主方法线程2
主方法线程3
run方法执行了3
主方法线程4
run方法执行了4
run方法执行了5
主方法线程5
run方法执行了6
主方法线程6
run方法执行了7
主方法线程7
run方法执行了8
主方法线程8
主方法线程9
run方法执行了9

实现callable接口

  • 声明一个实现Callable()方法的类,它是有返回值的,这个类需要重写call方法,
public class Demo implements Callable<Boolean>{//返回布尔类型的值

    public static void main(String[] args) throws Exception {
        Demo demo = new Demo();
        demo.call();
    }

    @Override
    public Boolean call()  {
        //返回当前线程的名字
        System.out.println("当前线程的名字"+new Thread().getName());
        for(int i =0 ; i<10;i++){
            System.out.println("run方法执行了"+i);

            //线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        return true;
    }
}

//执行结果
当前线程的名字Thread-0
run方法执行了0
run方法执行了1
run方法执行了2
run方法执行了3
run方法执行了4
run方法执行了5
run方法执行了6
run方法执行了7
run方法执行了8
run方法执行了9

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

线程操作API

isAlive

/**
* 测试此线程是否活动。如果线程已经启动并且还没有死亡,那么线程就是活的。
*
* 线程存活返回true,死亡返回false
*/
public final native boolean isAlive();
//查看threadDemo线程是否存活
boolean a = threadDemo.isAlive();
System.out.println(a);

setPriority

/**
* setPriority: 更改线程的优先级
* 参数说明:
*		newPriority:将线程设置为的优先级
*		
*/
public final void setPriority(int newPriority)
//将threadDemo线程顺序设为1
threadDemo.setPriority(1);

getPriority

/**
* 返回此线程的优先级。
*/
public final int getPriority()
package com.it.www.threadapp;

public class Demo3 {

	public static void main(String[] args) {
		
		MyThread03   mt1 = new  MyThread03("T1");
		MyThread03   mt2 = new  MyThread03("T2");
		MyThread03   mt3 = new  MyThread03("T3");
		
		//设置线程的优先级
		mt1.setPriority(10);
		mt2.setPriority(Thread.NORM_PRIORITY);
		mt3.setPriority(Thread.MIN_PRIORITY);
		
		//启动三个子线程
		mt1.start();
		mt2.start();
		mt3.start();
		
		//线程是存在一个执行的优先级别,是通过一个数值来进行表示的,数值越大,执行的优先级就越高
		int   priv1 = mt1.getPriority();
		int   priv2 = mt2.getPriority();
		int   priv3 = mt3.getPriority();
		
		//每个新创建的线程,默认的优先级是 5  ,其范围是1----10。
		System.out.println(priv1+"-----"+priv2+"----"+priv3);

	}

}
//定义一个线程类
class    MyThread03  extends  Thread{
	
	//通过构造的形式给线程取个名称
	public  MyThread03(String  name){
		super(name);
	}
	
	@Override
	public void run() {
		
		for(int i=1;i<=5;i++){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-----------"+i);
		}
		
	}
	
}

提示!

线程的执行与否,关联到许多的因素,其中线程的优先级也是执行机会提升的一个很重要的因素,但是并不是绝对性的,也就说不一定就是优先级高的线程首先执行完毕。

sleep

/**
* 使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统定时器和调度程序的精度和准确性。
* 线程不会失去任何显示器的所有权 
*/
public static native void sleep(long millis) throws InterruptedException;
//设置线程休眠时间:2000毫秒
Thread.sleep(2000);

join

/**
* 等待线程等待。
*/
public final void join() throws InterruptedException 
package com.it.www.threadapp;

public class Demo4 {

	public static void main(String[] args) {

		// 创建一个子线程
		MyThread04 mt1 = new MyThread04("T1");
		// 启动三个子线程
		mt1.start();

		for (int i = 1; i <= 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-----------"
					+ i);
		}

	}

}

// 定义一个线程类
class MyThread04 extends Thread {

	// 通过构造的形式给线程取个名称
	public MyThread04(String name) {
		super(name);
	}

	@Override
	public void run() {

		for (int i = 1; i <= 5; i++) {
			if (i == 3) {
				try {
					//合并线程(当前这个子线程被合并了,就是后续的操作不再执行,回到未执行完毕的主线程上继续以单线程的形式进行执行)
					this.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-----------"
					+ i);
		}
	}
}

yield

/**
* 暂停当前正在执行的线程对象,并执行其他线程
*/
public static native void yield();
package com.it.www.threadapp;

public class Demo5 {

	public static void main(String[] args) {

		// 创建一个子线程
		MyThread05 mt1 = new MyThread05("T1");
		// 启动三个子线程
		mt1.start();

		for (int i = 1; i <= 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-----------"
					+ i);
		}

	}

}

// 定义一个线程类
class MyThread05 extends Thread {

	// 通过构造的形式给线程取个名称
	public MyThread05(String name) {
		super(name);
	}

	@Override
	public void run() {

		for (int i = 1; i <= 5; i++) {
			if (i == 3) {
					//让出执行权,其他线程可以获取到CPU的执行权,从而进行执行,但是也有可能,当前这个线程再次获取到执行权,继续进行执行;
					this.yield();
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-----------"
					+ i);
		}

	}
}

stop

/**
* 强制使正在运行的线程停止运行(已停用)
*/
public final void stop() {}
if (a==55){
    this.stop();//当执行到55的时强制停止线程
}

wait 、 notify 、 notifyAll

都是用于控制线程同步的方法,后面进行讲解。

线程状态转换

图示结构:

在这里插入图片描述

说明:

线程共有5种状态: 创建 、 就绪状态 、 运行状态 、 阻塞状态 、 死亡状态

创建好一个线程之后,调用start方法并不是就进入了运行状态,而是进入了就绪状态(就是具备线程执行的一切条件)。再操作系统的分配中才会进入到执行的状态。由于各种情况可能会导致阻塞,解除阻塞后进入的是就绪状态,再进入到运行的状态。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340