Java线程创建的四种方式总结
作者:威斯布鲁克.猩猩 发布时间:2023-10-29 19:36:03
多线程的创建,方式一:继承于Thread类
1.创建一个继承于Thread类的子类
2.重写Thread类的run()--->将此线程执行的操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start():
start()方法的两个作用:
A.启动当前线程
B.调用当前线程的run()
创建过程中的两个问题:
问题一:我们不能通过直接调用run()的方式启动线程
问题二:在启动一个线程,遍历偶数,不可以让已经start()的线程去执行,会报异常;正确的方式是重新创建一个线程的对象。
//1.创建一个继承于Thread类的子类
class MyThread extends Thread{
//2.重写Thread类的run()
@Override
public void run() {//第二个线程
for(int i = 0;i < 10;i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {//主线程
//3.创建Thread类的子类的对象
MyThread t1 = new MyThread();
//4.通过此对象调用start()
t1.start();
//问题一:不能通过直接调用run()的方式启动线程
// t1.run();//错误的
//问题二:再启动一个线程:我们需要再创建 一个对象
//t1.start();//错误的
MyThread t2 = new MyThread();
t2.start();
for(int i = 0;i < 10;i++){
if(i % 2 != 0){
System.out.println(i + "****main()******");
}
}
}
}
此代码在主线程内输出奇数,在另一个线程里输出偶数,则输出结果应该是两个输出结果是交互的。
1****main()******
3****main()******
5****main()******
7****main()******
0
2
4
6
8
9****main()******
class Window extends Thread{//创建三个窗口卖票, 总票数为100张,使用继承于Thread类的方式
private static int ticket = 100;//三个窗口共享:声明为static
@Override
public void run() {
while(true){
if(ticket > 0){
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
public class WindowTest2 {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
public class ThreadDemo {
public static void main(String[] args) {
// MyThread1 m1 = new MyThread1();
// MyThread2 m2 = new MyThread2();
// m1.start();
// m2.start();
//由于造的类只创建过一次对象,后面就不用了,可以考虑使用匿名类的方式
//创建Thread类的匿名子类的方式
new Thread(){
@Override
public void run() {
for(int i = 0;i < 100;i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for(int i = 0;i < 100;i++){
if(i % 2 != 0){
System.out.println(i);
}
}
}
}.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for(int i = 0;i < 100;i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for(int i = 0;i < 100;i++){
if(i % 2 != 0){
System.out.println(i);
}
}
}
}
创建多线程的方式二:实现Runnable接口
创建一个实现了Runnable接口的类
实现类去实现Runnable中的抽象方法:run()
创建实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
通过Thread类的对象调用start()
class MThread implements Runnable{
//2.实现类去实现Runnable中的抽象方法:run()
@Override
public void run() {
for(int i = 0;i < 100;i++){
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//3.创建实现类的对象
MThread mThread = new MThread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mThread);
t1.setName("线程1");
//5.通过Thread类的对象调用start():A.启动线程B.调用当前线程的run()-->调用了Runnable类型的target
t1.start();
//再启动一个线程,遍历100以内的偶数//只需重新实现步骤4,5即可
Thread t2 = new Thread(mThread);
t2.setName("线程2");
t2.start();
}
}
class window1 implements Runnable{//创建三个窗口卖票, 总票数为100张,使用实现Runnable接口的方式
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
window1 w = new window1();//只造了一个对象,所以100张票共享
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
创建线程的方式三:实现Callable接口---JDK5.0新增
与使用Runnable相比,Callable功能更强大些
>相比run()方法,可以有返回值
>方法可以抛出异常
>支持泛型的返回值
>需要借助FutureTask类,比如获取返回结果
Future接口
>可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
>FutureTask是Futrue接口的唯一的实现类
>FutureTaskb同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for(int i = 1;i <= 100;i++){
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;//sum是int,自动装箱为Integer(Object的子类)
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象作为参数传递到 FutureTask的构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将 FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//获取Callable中call()的返回值(不是必须的步骤)
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
创建线程的方式四:使用线程池--->JDK5.0新增
背景:经常创建和销毁、使用量特别大的资源,比如并 * 况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:>提高响应速度(减少了创建新线程的时间)
>降低资源消耗(重复利用线程池中线程,不需要每次都创建)
>便于线程管理:A.corePoolSize:核心池的大小 B.maximumPoolSize:最大线程数 C.keepAliveTime:线程没有任务时最多保持多长时间后会终止
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程操作。需要提供实现Runnable 接口或Callable接口实现类的对象
service.execute(new NumberThread());//适用于Runnable
service.execute(new NumberThread1());//适用于Runnable
// service.submit(Callable callable);//适用于Callable
//3.关闭连接池
service.shutdown();
}
}
比较创建线程的两种方式:
开发中:优先选择:实现Runnable接口的方式
原因:1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。
系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。---生命周期
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
线程作为调度和执行的单位,每个线程拥有独立的运行栈和计数器,每个进程拥有独立的方法区和堆;意味着,多个线程共享一个方法区和堆。而共享的就可以优化,同时,共享的也会带来安全隐患,这就需要我们解决线程安全问题
背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需使用多线程呢?
使用多线程的优点:
1.提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2.提高计算机系统CPU的利用率
3.改善程序结构。将即长又复杂的线程分为多个线程,独立运行,利于理解和修改
何时需要多线程
1.程序需要同时执行两个或多个任务。
2.程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
3.需要一些后台运行的程序时。
public class Sample{
public void method1(String str){
System.out.println(str);
}
public void method2(String str){
method1(str);
}
public static void main(String[] args){
Sample s = new Sample();
s.method2("hello!");
}
}
注意:此程序不是多线程!main方法中调用了method1,method1中又调用了method2;是一条执行路径,所以是单线程
测试Thread类的常用方法:
1.start():启动当前线程:调用当前线程的run()
2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前CPU的执行权(下一刻CPU执行的线程仍是随机的)
>暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
>若队列中没有同优先级的线程,忽略此方法
7.join():在线程a中调用线程b的join(),此时,线程a就进入阻塞状态(停止执行),直到线程b完全执行完以后,线程b才结束阻塞状态(开始执行)。
8.sleep(long millitime):让当前线程"睡眠"指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。会抛出InterruptedException异常
* 9.isAlive():判断当前线程是否存活
class HelloThread extends Thread{
@Override
public void run() {
for(int i = 0;i < 100;i++){
if(i % 2 != 0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" +Thread.currentThread().getPriority() + ":" + i);
}
}
}
public HelloThread(String name){
super(name);
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread("Thread:1");//通过构造器给线程命名,但前期是得在子类中提供一个构造器
// h1.setName("线程一");
//设置分线程的优先级
h1.setPriority(Thread.MAX_PRIORITY);
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
for(int i = 0;i < 100;i++){
if(i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
}
// if(i == 20){
// try {
// h1.join();//join()的测试
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
}
}
线程的优先级:
1.MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5--->默认优先级
2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下,不一定被执行,并不意味着只有当高优先级的线程执行完毕后,低优先级的线程才执行。
来源:https://blog.csdn.net/weixin_49329785/article/details/119323345


猜你喜欢
- 简介:本篇博客主要包括recyclerview添加多种布局以及添加头布局和尾布局,还有item点击事件思路:主要重写Recyclerview
- 简介反射是Java编程语言中的一个特性。它允许执行的Java程序检查或 操作 自身,并操作程序的内部属性。例如,Java类可以获取其所有成员
- web采集的数据为 %u6B63%u5F0F%u4EBA%u5458,需要读取并转换为python对象,想了下不调用Javascript去e
- 本文实例为大家分享了javaweb多文件上传及zip打包下载的具体代码,供大家参考,具体内容如下项目中经常会使用到文件上传及下载的功能。本篇
- Android实现分享长图并且添加全图水印前言:长图一般是ScrollView和ListView。 我们需要取得这两个控件的完整显示的图片。
- 在搭建Spring Cloud Eureka环境前先要了解整个架构的组成,常用的基础模式如下图:服务提供者:将springboot服务编写好
- 以一个M×N的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的
- 使用CachePut注解,该方法每次都会执行,会清除对应的key值得缓存(或者更新),分为以下两种情况:如果返回值null,下次进行该key
- 网络办公正逐渐成为常态,无纸化办公也是一个潮流,这二者需要电子签章,最简单的方法就是在纸上盖一个章然后扫描成电子图片文件,最后在你的系统加载
- 流程引擎对象和其配置对象都是activiti的核心对象一、activiti的简单使用流程activiti在工作时,一般有以下几个步骤:创建一
- 进行GC性能调优时, 需要明确了解, 当前的GC行为对系统和用户有多大的影响。有多种监控GC的工具和方法, 本章将逐一介绍常用的工具。JVM
- Sentinel 是什么随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统
- 接收从控制台输入的数据可以使用Scanner类实现,Scanner类在一个名为util的包中需要在程序中导入这个包, 即在程序中添加impo
- 本文简单介绍如何动态创建接口interface的实现实例对象,包含两个知识点:1.如何获取接口interface的所有实现实例对象?2.如何
- 数据库里面表的字段中带有“”_“下划线,我们知道插件默认的是将这些带有下划线的字段默认的变成“优美的驼峰式”的。表是肯定不能动的,实体类的字
- 前言一入 Android 深似海,相信很多 Android 开发者深有体会,Android 系统版本的碎片化,Android 硬件设备的多样
- 摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的。现在我们来看如何利
- 本文实例讲述了C#判断系统是32位还是64位的方法。分享给大家供大家参考。具体如下:public static int GetOSBit()
- /* String name = "adsbsadgsadgtewterfsdf"
- Feign其他功能-超时设置Feign 底层依赖于 Ribbon 实现负载均衡和远程调用。Ribbon默认1秒超时。超时配置:ribbon: