Java——多线程详解(一)

引言

2021年3月2日21:55:46
知识均总结与: Java全栈学习网站. 以及自己的一些心得体会

基本概念

  1. 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。

  2. 实现多线程是采用一种并发执行机制 :
    并发执行机制原理:简单地说就是把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,从而达到多个应用程序在同时进行的效果 。
    多线程: 是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。这就是多线程程序 。

  3. 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态概念。

  4. 进程则是执行程序的一次过程,他是一个动态概念,是系统资源分配的单位

  5. 通常一个进程包含若干线程,线程是CPU调度和执行的单位。

  6. main()线程称为主线程,是系统的入口,用于执行整个程序。

  7. 线程的调度由调度器安排调度,调度器是与操作系统密切相关的,无法人为干预。

创建线程

三种方法:继承Thread类、实现Runnable接口、实现callable接口

继承Thread类

第一步:继承Thread类
第二步:重写run方法
第三步:创建对象,调用start开启线程

package work;

//创建线程方法一:继承Thread,重写run方法,调用start开启线程

//线程开启不一定立即执行,由cpu调度执行
public class Test extends Thread{
	
	
	public void run() {
		//run()方法线程的载体。不用显示调用,开启线程后会自动执行。
		for(int i = 0; i < 100; i++) {
			System.out.println("学习多线程"); 
		}
	}
	
	public static void main(String[] args) {
		Test thread1 = new Test();
		thread1.start();
		
		
		for(int i = 0; i < 1000; i++ ) {
			System.out.println("我是主线程");
		}
	}
}

注:不建议使用,避免单继承局限性

实现Runnable接口

第一步:定义一个类实现Runnable接口
第二步:实现run()方法
第三步:创建线程对象,调用start开启线程

package work;

//创建线程方法二

//线程开启不一定立即执行,由cpu调度执行
public class Test implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		for(int i = 0; i < 100; i++) {
			System.out.println("努力学习");
		}
		
	}
	
	public static void main(String[] args) {
		Test t1 = new Test();
		
		new Thread(t1).start();
		
		for(int i = 0; i < 1000; i++ ) {
			System.out.println("我是主线程");
		}
	}
}

注:推荐使用,避免了单继承的局限性,方便同一对象被多个线程使用

练习:模拟龟兔赛跑

package work;


//练习,模拟龟兔赛跑
public class TestThread   implements Runnable {
	
	private static String winner;
	
	@Override
	public void run() {
		// TODO 自动生成的方法存根
		
		for(int i = 0; i <= 1000; i++) {
			
			if(Thread.currentThread().getName().equals("兔子") && i%10==0) { //模拟兔子睡觉
				
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				
			}
			
			if(gameOver(i)) {
				break;
			}
			
			System.out.println(Thread.currentThread().getName() + "---->跑了" +  i + "步" );
		}
	}
	//判断胜负
	private boolean gameOver(int steps) {
		
		if(winner != null) { //出现胜利者
			return true;
		}
		
		if (steps >= 1000) {
			winner = Thread.currentThread().getName();
			System.out.println("winner is " + winner);
			return true;
		}
		
		return false;
	}
	
	public static void main(String[] args) {
		TestThread t1 = new TestThread();
		
		new Thread(t1,"兔子").start();
		new Thread(t1,"乌龟").start();
	}

}

实现Callable接口(了解)

步骤:

  1. 实现Callable 接口,需要返回值类型
public class TestThread   implements  Callable<Boolean> {
  1. 重写call方法,需要抛出异常,注意返回值类型要与接口那里一致
public Boolean call() {
  1. 创建目标对象

  2. 创建执行服务

ExecutorService ser = Executors.newFixedThreadPool(1);//1表示创建一个线程
  1. 提交执行
Future<Boolean> r1 = ser.submit(t1);
  1. 获取结果
boolean rs1 = r1.get();
  1. 关闭服务
ser.shutdownNow();
package work;

import java.util.concurrent.*;

//线程创建方式三,创建Callable接口
public class TestThread   implements  Callable<Boolean> {
	
	private static Object ExecutorService;

	public Boolean call() {
		for(int i = 0; i < 1000; i++) {
			System.out.println("我是Callable接口实现的线程");
		}
		return true;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		TestThread t1 = new TestThread();
		
		//创建执行服务
		ExecutorService ser = Executors.newFixedThreadPool(1);
		
		//提交执行
		Future<Boolean> r1 = ser.submit(t1);
		
		//获取结果
		boolean rs1 = r1.get();
		
		//关闭服务
		ser.shutdownNow();
		
		for(int i = 0; i < 1000 ; i++) {
			System.out.println("我是主线程");
		}
		
	}

}

静态代理模式

这是一种思想,真实对象专注于自己的事情,代理对象去处理真实对象做不了的事情
前面的Runnable接创建线程,就是利用了这种思想。

package work;

//总结
/*
 * 真实对象和代理对象都要实现同一个接口
 * 我们的在前面使用Runnable接口创建线程时,就使用了这种思想
 * 
 * */

public class Test{
	public static void main(String[] args) {
		You you = new You();
		new Company(you).happyMarry();
	}
}

interface Marry{
	void happyMarry();
}


class You implements Marry{
	public void happyMarry() {
		System.out.println("开开心心结婚了");
	}
}


class Company implements Marry{
	
	private Marry target;
	
	public Company(Marry target) {
		this.target = target;
	}
	
	public void happyMarry() {
		
		before();
		this.target.happyMarry();
		after();
	}
	
	public void before() {
		System.out.println("布置现场");
	}
	
	public void after() {
		System.out.println("收钱");
	}
}

Lamda表达式(类的一种书写方式)

好处:
避免匿名内部类定义过多
代码更简洁,只留下核心的逻辑

函数式接口:接口中只包含一个抽象方法,那么它就是一个函数式接口,对于函数式接口,我们可以使用Lambda表达式创建该接口的对象。

interface Marry{
	void happyMarry();
}

我们的Runnable接口同样可以用Lambda表达式创建对象。

例子:

package work;

/*
 *推导Lambda表达式 
 */

public class Test{
	//3.静态内部类
	static class Love1 implements ILove{
		public void lambda(int a) {
			System.out.println("Lvoe----"+a);
		}
	}

	
	
	public static void main(String[] args) {
		ILove love = new Love();
		love.lambda(0);
		
		
		love = new Love1();
		love.lambda(1);
		
		//4.局部内部类
		class Love2 implements ILove{
			public void lambda(int a) {
				System.out.println("Lvoe----"+a);
			}
		}
		love = new Love2();
		love.lambda(2);
		
		//5.匿名内部类,没有类的名称,必须借助接口或者父类
		love = new ILove(){
			public void lambda(int a) {
				System.out.println("Lvoe----"+a);
			}
		};
		love.lambda(3);
		
		
		//6.用Lambda表达式简化
		love = (int a)->{
			System.out.println("Lvoe----"+a);
		};
		love.lambda(4);
		
		
		//Lambda表达式的简写    省略参数,如果有多个参数,要么都省,要么都不省
		love =love = (a)->{
			System.out.println("Lvoe----"+a);
		};
		love.lambda(5);
		
		//省略括号,只有一个参数可以省略括号
		love =love = a->{
			System.out.println("Lvoe----"+a);
		};
		love.lambda(6);
		
		//如果只有一条语句,可以省略花括号
		love =love = a->System.out.println("Lvoe----"+a);
		love.lambda(7);
	}

}

//1.定义一个接口
interface ILove{
	void lambda(int a);
}

//2.实现类
class Love implements ILove{
	public void lambda(int a) {
		System.out.println("Lvoe----"+a);
	}
}




线程状态

五状态图
在这里插入图片描述
注:

  1. 线程对象一旦创建就进入新生状态
  2. 调用satart()方法后进入就绪状态
线程方法
停止线程

尽量使用自己的方法去终止线程

package work;

/*
 *1.建议线程正常停止,-----》利用次数,不建议死循环
 *2.建议使用标识位--------》设置一个标志位
 *3.不建议使用stop或destroy等过时的或者JDK不建议使用的方法 
 */
public class Test implements Runnable{
	
	//1.设置一个标识位
	private boolean flag = true;
	
	public void run() {
		int i = 0;
		while(flag) {
			System.out.println("run----"+i++);
		}
	}
	
	//2.设置一个公共的方法停止线程,转换标志位
	public void stop() {
		this.flag = false;
	}
	
	//主线程
	public static void main(String[] arg) {
		Test t1 = new Test();
		new Thread(t1).start();
		
		for(int i = 0; i < 1000; i++) {
			System.out.println("main thread"+i);
			
			if(i == 900) {
				//调用stop方法切换标志位,让线程终止
				t1.stop();
				System.out.print("==========线程终止了===========");
			}
		}
	}
	
}

线程休眠
  1. sleep(时间)指定当前线程阻塞的毫秒数
  2. sleep存在异常InterruptedException
  3. sleep时间到达后线程进入就绪状态
  4. 可以模拟网络延时,倒计时等
  5. 每个对象都有一个锁,sleep不会释放锁

模拟倒计时

package work;

import java.text.SimpleDateFormat;
import java.util.Date;

//倒计时
public class Test_1 {
	 
	public static void main(String[] args) {
		
		//打印当前系统时间
		Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
		
		while(true) {
			try {
				
				Thread.sleep(1000);
				System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
				startTime = new Date(System.currentTimeMillis());//更新当前时间
				
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
	
	public static void tenDown() {
		int num = 10;
		
		while(true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			System.out.println(num--);
			if(num<=0) {
				break;
			}
		}
	}
}

线程礼让(yield)
  1. 让当前正在执行的线程暂停,但不阻塞
  2. 将线程从运行态转为就绪态
  3. 让cpu重新调度,礼让不一定成功
package work;

public class Test_2 {
	public static void main(String[] args) {
		
		MyYield myyield = new MyYield();
		
		new Thread(myyield,"a").start();
		new Thread(myyield,"b").start();
		
	}

}


class MyYield implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		System.out.println(Thread.currentThread().getName()+"线程开始执行");
		Thread.yield();
		System.out.println(Thread.currentThread().getName()+"线程停止执行");
	}
	
}
线程强制执行(join)
  1. 待此线程执行完毕后,在执行其他线程,其他线程阻塞
  2. 可以想象成插队
package work;

public class Test_3 implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		for(int i = 0; i<=200; i++) {
			System.out.println("VIP来了----" + i);
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		//启动我们的线程
		Test_3 t1 = new Test_3();
		Thread thread = new Thread(t1);
		thread.start();
		
		
		//主线程
		for(int i = 0; i< 500; i++) {
			if(i == 200) {
				thread.join(); //插队
			}
			System.out.println("main------"+i);
		}
	}

}

观测线程状态
  1. NEW:没有启动的线程处于此状态
  2. RUNNABLE:Java虚拟机中执行的线程处于此状态
  3. BLOCKED:阻塞的线程处于此状态
  4. WAITING:等待另一线程执行特定动作的线程处于此状态
  5. TIMED_WAITING:等待另一线程执行动作达到指定等待时间的线程
  6. TERMINATED:已退出的线程
  7. 通过thread.getState(); 获取线程当前状态

退出的线程无法再次被启动

package work;

//观察测试线程状态
public class Test_4 {
	public static void main(String[] args) throws InterruptedException {
		
		Thread thread  = new Thread(()-> {
			for(int i = 0; i < 5; i++) {
				try {
					Thread.sleep(1000);
					//TIMED_WAITING 阻塞
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
			System.out.println("");
		});
		
		
		//观察状态
		Thread.State state = thread.getState();  //获取线程当前状态
		System.out.println(state);  //NEW
		
		//观察启动后
		thread.start();
		state = thread.getState();
		System.out.println(state); //RUNNABLE
		
		while(state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出状态
			Thread.sleep(100);
			state = thread.getState();
			System.out.println(state);
		}
	}

}

线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调用哪个线程来执行

线程的优先级用数字表示(1——10)

  1. Thread.MAX_PRIORITY=10
  2. Thread.MIN_PRIORITY=10
  3. Thread.NORM_PRIORITY=10

改变优先级的方式与获取优先级
getPriority()、setPriority()

优先级高的不一定先执行

package work;

//测试线程优先级
public class Test_5 {
	
	public static void main(String[] args) {
		
		//主线程默认优先级
		System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
		
		MyPriority t = new MyPriority();
		
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		Thread t5 = new Thread(t);
		Thread t6 = new Thread(t);
		
		t1.start();
		
		t2.setPriority(1);
		t2.start();
		
		t3.setPriority(4);
		t3.start();
		
		t4.setPriority(Thread.MAX_PRIORITY);   //MAX_PRIORITY=10
		t4.start();
		
		
		t5.setPriority(Thread.MIN_PRIORITY);   //MIN_PRIORITY=1
		t5.start();
		
		t6.setPriority(Thread.NORM_PRIORITY);   //NORM_PRIORITY=5
		t6.start();
	}
}


class MyPriority implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority());
	}
	
}
守护线程

线程必须分为:用户线程、守护线程

  1. 虚拟机必须保护用户线程执行完毕
  2. 虚拟机不必等待守护线程执行完毕
  3. 守护线程:如,后台记录操作日志,垃圾回收等待等。
package work;
//测试守护线程
public class Test_6 {
	public static void main(String[] args) {
		God god = new God();
		You you = new You();
		
		Thread thread = new Thread(god);
		thread.setDaemon(true); //默认false表示用户线程,正常的线程都是用户线程
		thread.start();  //上帝守护线程启动
		
		
		new Thread(you).start();
		
		
	}

}

class God implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		while(true) {
			System.out.println("上帝守护你");
		}
	}
	
}

class You implements Runnable{

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		for(int i = 0; i < 36500; i++) {
			System.out.println("开开心心过完一生");
		}
		
		System.out.print("goodbye  world!");
		
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值