java线程的理解和运用

本文详细介绍了计算机中程序、进程和线程的概念,展示了线程的创建方式(Runnable接口和Thread类),线程的实现(包括Callable接口和线程池),以及线程的各种方法如命名、休眠、优先级、中断、让出和分类。
摘要由CSDN通过智能技术生成

什么是线程

首先要了解计算机中的程序、进程、线程分别是什么

  • 程序:计算机指令的集合;
  • 进程:计算机以“进程”的方式运行程序,并分配“CPU”、“网络”、“磁盘”、“内存”等资源;
  • 线程:一个进程中会包含多个进程

线程的创建和实现

线程的创建

创建Thread对象就是创建一个线程(创建Thread对象的两种方法)

方法一:通过new一个Runnable接口的实现类

	Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(char c='a';c<'z';c++) {
					System.out.println(Thread.currentThread().getName()+":"+c);
				}
			}
		},"子线程1");

需要注意的是Runnable接口实现类的run方法没有返回值

方法二:通过创建Thread类的子类

		Thread thread2 = new Thread("子线程2") {
			@Override
			public void run() {
				for(int c=1;c<26;c++) {
					System.out.println(Thread.currentThread().getName()+":"+c);
				}
			}
		};
线程的实现

1.通过实现Runnable接口(实现Runnable接口,无返回值)

2.继承Thread类

3.实现Callable接口(子线程执行后,有返回值)

  • 先创建Callable接口的实现类
  • 再创建Callable接口实现类的对象
  • 再将Callable接口实现类的对象转换为FutureTask(相当于是runnable接口实现类,因为Thread对象的有参构造方法只要Runnable)

这里举例说明(使用多线程的方式实现1-10000的和)

package Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test03 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//使用多线程的方式实现1-10000的和
		
		
		//创建callable对象
		SubTask subTask1 = new SubTask(1, 2000);
		SubTask subTask2 = new SubTask(2000, 4000);
		SubTask subTask3 = new SubTask(4000, 6000);
		SubTask subTask4 = new SubTask(6000, 8000);
		SubTask subTask5 = new SubTask(8000, 10001);
		
		//将callnable对象转换为FutureTask(相当于是runnable接口实现类)
		
		FutureTask<Integer> futureTask1 = new FutureTask<Integer>(subTask1);
		FutureTask<Integer> futureTask2 = new FutureTask<Integer>(subTask2);
		FutureTask<Integer> futureTask3 = new FutureTask<Integer>(subTask3);
		FutureTask<Integer> futureTask4 = new FutureTask<Integer>(subTask4);
		FutureTask<Integer> futureTask5 = new FutureTask<Integer>(subTask5);
		
		
		//创建thread对象
		Thread thread1 = new Thread(futureTask1);
		Thread thread2 = new Thread(futureTask2);
		Thread thread3 = new Thread(futureTask3);
		Thread thread4 = new Thread(futureTask4);
		Thread thread5 = new Thread(futureTask5);
		
		//启动线程
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		
		//拿到结果
		Integer sum1 = futureTask1.get();
		Integer sum2 = futureTask2.get();
		Integer sum3 = futureTask3.get();
		Integer sum4 = futureTask4.get();
		Integer sum5 = futureTask5.get();
		
		int total = sum1+sum2+sum3+sum4+sum5;
		System.out.println(total);
	}
}

class SubTask implements Callable<Integer>{
	private int begin,end;
	
	public SubTask(int begin,int end) {
		this.begin = begin;
		this.end = end;
	}
	
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for(int i= begin;i<end;i++) {
			sum += i;
		}
		return sum;
	}
	
}

4.线程池(提高线程的使用效率)

package Thread;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test04 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//ExecutorService:线程池
		//Executors:工具类,用于创建各种形式的线程池
		//Executors.newFixedThreadPool(5): 创建固定线程数目的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(5);
		
		int total = 0;
		for(int i=1;i<=10000;i+=2000) {
			Future<Integer> submit = executorService.submit(new SubTask(i,i+2000));
			Integer sum = submit.get();
			total +=sum;
		}
		System.out.println(total);
	}
}

线程的方法

获取当前线程

获取当前线程 —— Thread.currentThread() —— 通过方法可以获取当前线程的信息

线程命名

线程的命名有两种方法

  1. 在创建Thread对象时直接命名

    package Thread;
    
    public class Demo01 {
    	public static void main(String[] args) {
    		Thread thread = new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				
    			}
    		},"子线程一");
    	}
    }
    
    
  2. 在创建完线程后通过setName()方法命名

    package Thread;
    
    public class Demo01 {
    	public static void main(String[] args) {
    		Thread thread = new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				
    			}
    		});
    		thread.setName("子线程一");
    	}
    }
    
    
线程休眠

线程休眠是在代码运行开始时进行休眠,当休眠时间结束时线程自动恢复到RUNNABLE运行状态

线程休眠(参数单位是毫秒) —— Thread.sleep(1000)

线程优先级

当设置线程优先级方法后,线程会优先执行(只是参考,不会完全优先执行)

设置优先级的方法(优先级最高为10、最低为1,默认为5) —— 线程对象.setPriority(1)

package Thread;

public class Test05 {
	public static void main(String[] args) {
		MyTask myTask = new MyTask();
		
		Thread thread1 = new Thread(myTask);
		Thread thread2 = new Thread(myTask);
		
		//设置优先级
		thread1.setPriority(1);
		thread2.setPriority(10);
		
		thread1.setName("线程一");
		thread2.setName("线程二");
		thread1.start();
		thread2.start();
	}
}

class MyTask implements Runnable{
	@Override
	public void run() {
		char c = '王';
		for(int i = 0;i<=26;i++,c++) {
			System.out.println(Thread.currentThread().getName()+":"+c);
		}
	}
}

线程的插队

因为JVM虚拟机是争抢机制,所以线程没有顺序可言,但是线程的插队会使执行插队方法的线程对象优先执行

线程插队 —— 线程对象.join();join()方法通过wait()实现,是被插队的线程(主线程),处于等待状态。

package Thread;

public class Test06 {
	public static void main(String[] args) {
		MyTask myTask = new MyTask();
		Thread thread = new Thread(myTask);
		thread.start();
		
		try {
			thread.join(); //子线程插队
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//等子线程执行后,主线程恢复执行
		System.out.println("main,主线程结束");
	}
}

线程的中断

线程中断方法会使该线程中断允许,通过 线程对象.interrupt() 方法的执行来设置当前线程为中断状态,当线程中断时,会抛出InterruptedException异常,同时可以通过isInterrupted()方法来判断当前线程是否为状态。

package Thread;

public class Test07 {
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("子线程开始执行");
				
				
//				try {
//					Thread.sleep(6000); //子线程休眠
//				} catch (InterruptedException e) {
//					System.out.println("子线程中断");
//				}
				
				//isInterrupted()检查当前线程是否中断
				while(!Thread.currentThread().isInterrupted()) {
					System.out.println("999");
				}
				
				System.out.println("子线程结束");
			}
		});
		thread.start();
		
		try {
			Thread.sleep(3000);//主线程休眠
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		thread.interrupt(); // 线程中断
		
		System.out.println("主线程结束");
	}
}

线程的让出

线程调度模型:

  1. 分时调度模型:所有线程轮流获取CPU使用权,平均分配每个线程占用的CPU时间片
  2. 抢占调度模型:优先分配给优先级较高的线程占用CPU,如果优先级相同,则随机选择一个线程;JVM虚拟机采用抢占调度模型;

线程的让出 (线程的让出也不是绝对的) —— Thread.yield()

package Thread;

public class Test08 {
	public static void main(String[] args) {
		Thread thread1 = new Thread(new Runnable() {

			@Override
			public void run() {

				for (char c = 'A'; c < 'Z'; c++) {
					Thread.yield(); //让出线程CPU处理器的执行
					System.out.println(Thread.currentThread().getName()+":"+c);
				}
			}
		},"线程1");

		Thread thread2 = new Thread(new Runnable() {

			@Override
			public void run() {

				for (char c = 'a'; c < 'z'; c++) {
					System.out.println(Thread.currentThread().getName()+":"+c);
				}
			}
		},"线程2");
		
		thread1.start();
		thread2.start();
	}
}

线程的分类
  • 用户线程:所有用户线程执行结束后,JVM虚拟机会自动退出;
  • 守护线程:守护线程执行结束后,JVM虚拟机不会自动退出;使用setDaemon(true)方法

线程的状态

  • NEW(新建状态):调用构造方法后,线程处于新建状态;
  • RUNNABLE(运行状态):调用线程的start()方法后,线程处于可运行状态;
  • BLOCKED(阻塞状态):两个线程同时竞争synchronized锁时,获取锁的线程处于RUNNABLE运行状态,未获取锁的线程处于BLOCKED阻塞状态;
  • WAITING(等待状态):必须通过notify()或notifyAll()
  • TIMED_WAITING(计时等待状态):等待时间结束后,自动唤醒
  • TERMINATED(终止状态)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

户线程执行结束后,JVM虚拟机会自动退出;

  • 守护线程:守护线程执行结束后,JVM虚拟机不会自动退出;使用setDaemon(true)方法

线程的状态

  • NEW(新建状态):调用构造方法后,线程处于新建状态;
  • RUNNABLE(运行状态):调用线程的start()方法后,线程处于可运行状态;
  • BLOCKED(阻塞状态):两个线程同时竞争synchronized锁时,获取锁的线程处于RUNNABLE运行状态,未获取锁的线程处于BLOCKED阻塞状态;
  • WAITING(等待状态):必须通过notify()或notifyAll()
  • TIMED_WAITING(计时等待状态):等待时间结束后,自动唤醒
  • TERMINATED(终止状态)

在这里插入图片描述

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 建立三个线程,并且同时运行它们。当运行时输出线程的名称。 实验步骤: (1)、创建类sy6_1 (2)、创建三个线程,调用start()方法启动这三个线程 (3)、保存文件,调试并编译运行程序。 参考程序运行效果: 2. 实现3个类:Storage、Counter和Printer。 Storage类应存储整数。 Counter应创建线程线程从0开始计数(0,1,2,3…)并将每个值存储到Storage类中。 Printer类应创建一个线程线程读取Storage类中的值并打印值。编写程序创建Storage类的实例,并创建一个Counter对象和Printer对象操作此实例。 实验步骤: (1)、创建三个类Counter, Printer,Storage (2)、创建TestCounter类,在该类中定义main函数,在main函数中定义Storage对象、Counter对象和 Printer对象,创建Counter线程和Printer线程并启动 (3)、保存文件,调试并编译运行程序。 参考程序运行效果: 3. 修改实验1第2题的程序,添加适当代码,以确保每个数字都恰好只被打印一次。 实验步骤: (1)、创建三个类Counter, Printer,Storage (2)、 创建TestCounter类,在该类中定义main函数,在main函数中定义Storage对象、Counter1对象和 Printer对象,创建Counter线程和Printer线程并启动 (3)、在定义Storage类中的setValue(int i) 和getValue ()方法时使用synchronized关键字,将其定义为同步方法 (4)、保存文件,调试并编译运行程序。 参考程序运行效果:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值