Java多线程机制

目录

进程与线程

多线程 

主线程(main线程)

线程的创建和启动

线程的状态 

线程优先级 

线程调度 

          线程休眠

 线程的强制运行

线程的礼让

同步方法

同步代码块


进程与线程

        线程不是进程,但其行为很像进程,线程是比进程更小的执行单位,一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程。

        和进程可以共享操作系统的资源类似,线程间也可以共享进程中的某些内存单元(包括代码与数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作,但与进程不同的是,线程的中断与恢复可以更加节省系统的开销。

        具有多个线程的进程能更好地表达和解决现实世界的具体问题,多线程是计算机应用开发和程序设计的一项重要的实用技术。

        没有进程就不会有线程,就像没有操作系统就不会有进程一-样。尽管线程不是进程,但在许多方面它非常类似进程,通俗地讲,线程是运行在进程中的“小进程”。

多线程 

        如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”。多个线程交替占用CPU资源,而非真正的并行执行

多线程好处

        充分利用CPU的资源

        简化编程模型

        带来良好的用户体验

主线程(main线程)

        每个Java应用程序都有一个缺省的主线程。我们已经知道,Java 应用程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程称为“主线程”(main线程),该线程负责执行main方法。那么,在main方法的执行中再创建的线程,就称为程序中的其他线程。

        如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束我们的Java 应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句(主线程结束),JVM也不会结束Java应用程序,JVM一直要等到Java应用程序中的所有线程都结束之后,才结束Java应用程序

线程的创建和启动

在Java中创建线程的两种方式

        继承java.lang.Thread类

        实现java.lang.Runnable接口

使用线程的步骤

1.定义线程

2.创建线程对象

3.启动线程

4.终止线程

继承Thread类创建线程

        定义MyThread类继承Thread类

        重写run()方法,编写线程执行体

        创建线程对象,调用start()方法启动线程

多个线程交替执行,不是真正的“并行” ;线程每次执行时长由分配的CPU时间片长度决定

package demo04;
//编写线程类的第一步:创建MyThread类,继承Thread类
public class MyThread extends Thread{
	//第二步:重写Thread类的中的run();
	@Override
	public void run() {
		//第三步:在run()方法中写你要运行的代码
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
			
		}
	}
}

 

package demo04;

public class MyThreadTest {

	public static void main(String[] args) {
		// 创建线程类对象
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		
		mt1.setName("线程A");
		mt2.setName("线程B");
		//启动线程要使用start()方法来启动线程,如果通过线程对象直接调用run()方法,那是由主线程来调用的,不是多线程的实现方式
//		mt1.run();
		
		mt1.start();
		mt2.start();
		
		
		

	}

}

实现Runnable接口创建线程

        定义MyRunnable类实现Runnable接口

        实现run()方法,编写线程执行体

        创建线程对象,调用start()方法启动线程

 

package demo05;

//第一步:创建线程类实现Runnable类接口
public class MyRunnable implements Runnable{
	
	//第二步:重写run()方法
	@Override
	public void run() {
		//第三步:在run()方法中定义要执行的代码
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
		
	}

}
package demo05;

public class MyRunnableTest {

	public static void main(String[] args) {
		// 创建线程类对象
		MyRunnable mr = new MyRunnable();
		//MyRunnable类中没有start()方法,其父类Object类中也没有start()方法,其实现的接口Runnable中只有run()方法,没有start()方法
		//mr.start();
		
		Thread t1 =  new Thread(mr,"排山倒海");
		Thread t2 = new Thread(mr,"降龙十八掌");
		
		t1.start();
		t2.start();

	}

}

 比较两种创建线程的方式

        继承Thread类 编写简单,可直接操作线程

        适用于单继承

实现Runnable接口

        避免单继承局限性

        便于共享资源 

 推荐使用实现Runnable接口方式创建线程

线程的状态 

线程优先级 

线程优先级由1~10表示,1最低,默认优先级为5

优先级高的线程获得CPU资源的概率较大,不能保证每一次优先级高的线程先获取CPU资源

 

线程调度 

线程调度指按照特定机制为多个线程分配CPU的使用权

方       法

 说       明

void setPriority(int  newPriority)

更改线程的优先级

static void sleep(long millis)

在指定的毫秒数内让当前正在执行的线程休眠

void join()

等待该线程终止

static void yield()

暂停当前正在执行的线程对象,并执行其他线程

void interrupt()

中断线程

boolean isAlive()

测试线程是否处于活动状态

线程休眠

让线程暂时睡眠指定时长,线程进入阻塞状态 睡眠时间过后线程会再进入可运行状态

package demo02;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
			if(i==5){
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}
package demo02;
//通过线程睡眠来调度线程
public class MyRunnableTest {

	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();
		Thread t = new Thread(mr);
		
		t.start();

	}

}

 线程的强制运行

使当前线程暂停执行,等待其他线程结束后再继续执行本线程

public final void join()

public final void join(long mills)

public final void join(long mills,int nanos)

millis:以毫秒为单位的等待时长 nanos:要等待的附加纳秒时长

需处理InterruptedException异常 

package demo03;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <=100; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

}
package demo03;
//线程调度之强制执行
public class MyRunnableTest {

	public static void main(String[] args) {
		Thread t1 = new Thread(new MyRunnable(), "线程A");
		t1.start();
		
		for (int i = 1; i <=10; i++) {
			if(i==3){
				try {
//					线程强制执行:t1强制执行,一直等到t1执行完毕之后,才会释放CPU资源给其它线程执行,在调用join()方法之前,多个线程依然是抢占CPU
					t1.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+":"+i);
		}

	}

}

线程的礼让

暂停当前线程,允许其他具有相同优先级的线程获得运行机会 ;该线程处于就绪状态,不转为阻塞状态

只是提供一种可能,但是不能保证一定会实现礼让

public static void yield() 

 

package demo04;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+"正在运行:"+i);
			if(i==3){
				System.out.print("线程礼让:");
//				线程礼让:让当前执行run()方法的线程释放CPU资源,给其它线程一个获得CPU资源的机会,但是当前线程还会抢占CPU资源,也就是说,当前线程释放CPU资源后,会与其它线程再一次抢占CPU,就看其它线程能不能抓住这个机会
				Thread.yield();
			}
		}
	}

}
package demo04;
//线程调度之线程礼让
public class MyRunnableTest {

	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();
		Thread t1 = new Thread(mr, "线程A");
		Thread t2 = new Thread(mr, "线程B");
		
		t1.start();
		t2.start();

	}

}

同步方法

        将线程要操作的代码放入同步方法中,当线程执行到同步方法的时候,一定会执行完同步方法里的所有代码后再释放CPU资源,从而一个线程对数据的操作不会受其它线程的影响。

使用synchronized修饰的方法控制对类成员变量的访问

访问修饰符 synchronized 返回类型 方法名(参数列表){……}或者

synchronized 访问修饰符 返回类型 方法名(参数列表){……} 

package demo06;

public class Site implements Runnable {
	// 定义属性表示票库里的票的数量
	private int count = 10;
	// 定义属性表示用户买到的是第几张票
	private int num = 0;

	@Override
	public void run() {
		while (true) {
			if(!sale()){
				break;
			}
		}

	}


	public synchronized boolean sale() {
		// 如果票的数量小于0,就不在卖票
		if (count <= 0) {
			return false;
		}

		// 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
		count--;
		num++;
		// 买票过程中模拟网速缓慢
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "买到了第" + num
				+ "张票,还剩余" + count + "张票");

		return true;

	}

}

 

同步代码块

使用synchronized关键字修饰的代码块 

synchronized(syncObject){    

//需要同步的代码

}

syncObject为需同步的对象,通常为this 效果与同步方法相同 

多个并发线程访问同一资源的同步代码块时

同一时刻只能有一个线程进入synchronized(this)同步代码块

当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定

当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

package demo07;

public class Site implements Runnable {
	// 定义属性表示票库里的票的数量
	private int count = 10;
	// 定义属性表示用户买到的是第几张票
	private int num = 0;

	@Override
	public void run() {
		while (true) {
			synchronized(this){
				// 如果票的数量小于0,就不在卖票
				if (count <= 0) {
					break;
				}
				
				// 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
				count--;
				num++;
				// 买票过程中模拟网速缓慢
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "买到了第" + num
						+ "张票,还剩余" + count + "张票");
			}
		}

	}

}
package demo07;
//同步代码块
public class Test {

	public static void main(String[] args) {
		Site site = new Site();
		
		Thread t1 = new Thread(site, "张三");
		Thread t2 = new Thread(site, "李四");
		Thread t3 = new Thread(site, "王五");
		
		t1.start();
		t2.start();
		t3.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