java架构学习——1. 多线程快速入门

本篇博文主要包含:

  • 进程、线程及多线程的基本概念
  • 线程创建的五种方式
  • 线程常用api
  • 守护线程与非守护线程(用户线程)
  • 线程的五种状态
  • join()、yield()和priority()方法

一. 线程基本概念

  1. 进程:每个正在系统上运行的程序都是一个进程,如QQ等。每个进程包含一到多个线程。

  2. 线程:线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。即正在独立运行的一条执行路径。

  3. 多线程:能够在同一时间执行多于一个线程,每个线程都能独立运行,互不影响,进而提高了程序的效率,而不提高带宽速度。在多线程中如果其中一个线程抛异常执行中断,而其他线程却不会受其影响。
    一个操作系统中可以有多个进程,每个进程中一定会有一个决定代码执行顺序的主线程,如main函数。

  4. 线程创建有哪些方式:
    (1)继承Thread类
    (2)实现Runnable接口
    (3)使用匿名内部类
    (4)Callable类
    (5)使用线程池创建线程(企业中常用)
    使用实现Runnable接口比继承Thread类好,因为可以实现多个接口和继承一个类,另外还符合面向接口开发。

  5. 单线程与多线程的区别:单线程中代码从上往下执行,相当于同步,如果其中某个方法中的代码挂了,其下面的代码就不能继续执行了;多线程就相当于异步,同时可以执行多个方法,其中一个方法中的代码挂了,不会影响其他方法的执行。

二. 线程创建方式代码实现

  1. 继承Thread类
public class ThreadDemo{

	public static void main(String[] args) {
		//调用线程
		Thread01 thread01 = new Thread01();
		//启动线程,不是调用run方法,而是调用start方法。
		//启动线程后,代码不会从上往下执行。
		thread01.start();
		for(int i=0; i<5; i++) {
			System.out.println("main:"+i);
		}
	}
}

class Thread01 extends Thread{

	//run方法中编写多线程需要执行的代码
	@Override
	public void run() {
		for(int i=0; i<5; i++) {
			System.out.println("childern:"+i);
		}
	}
}

运行结果:
在这里插入图片描述

  1. 实现Runnable接口
public class RunnableDemo {
	public static void main(String[] args) {
		//调用线程
		Thread02 t = new Thread02();
		Thread thread = new Thread(t);
		//启动线程
		thread.start();
		for(int i=0; i<5; i++) {
			System.out.println("main:"+i);
		}
	}

}

class Thread02 implements Runnable{
	//run方法中编写多线程需要执行的代码
	@Override
	public void run() {
		for(int i=0; i<5; i++) {
			System.out.println("childern:"+i);
		}
	}
}

运行结果:
在这里插入图片描述

  1. 使用匿名内部类
public class AnonymousDemo {
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			//run方法中编写多线程需要执行的代码
			@Override
			public void run() {
				for(int i=0; i<5; i++) {
					System.out.println("childern:"+i);
				}
			}
		});
		//启动线程
		thread.start();
		
		for(int i=0; i<5; i++) {
			System.out.println("main:"+i);
		}
	}
}

运行结果:
在这里插入图片描述

  1. Callable类,相较于实现 Runnable 接口的方式,区别在于Callable有返回值并且可以抛出异常。
public class CallableDemo {
	 //相较于实现 Runnable 接口的方式,区别在于Callable有返回值并且可以抛出异常。
	 static class SumTask implements Callable<Long> {
	        @Override
	        public Long call() throws Exception {
	            long sum = 0;
	            for (int i = 0; i < 5; i++) {
	            	 sum += i;
	                System.out.println("childern: "+i);
	            }
	            System.out.println("sum: "+ sum);
	            return sum;
	        }
	    }

	    public static void main(String[] args) throws ExecutionException, InterruptedException {
	      //执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
	        FutureTask<Long> futureTask = new FutureTask<Long>(new SumTask());
	        //执行线程方式一:
	        /*Executor executor=Executors.newSingleThreadExecutor();
	        executor.execute(futureTask);*/
	        //执行线程执行线程方式二:
	        new Thread(futureTask).start();
	       
	        for (int i = 0; i < 5; i++) {
                System.out.println("main: "+i);
            }
	        System.out.println("返回值: "+futureTask.get());
	    }
}

运行结果:
在这里插入图片描述
Callable常用api

boolean cancel(boolean mayInterruptIfRunning)如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。参数:mayInterruptIfRunning - 如果应该中断执行此任务的线程,则为 true;否则允许正在运行的任务运行完成。返回值:如果无法取消任务,则返回 false,这通常是由于它已经正常完成;否则返回 true
boolean isCancelled()返回值:如果在任务正常完成前将其取消,则返回 true。
boolean isDone()返回值:如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
V get()返回值:如有必要,等待计算完成,然后获取其结果。
V get(long timeout,TimeUnit unit)返回值:如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

代码演示:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class CallableDemo {
	 //Callable和Runnbale一样代表着任务,区别在于Callable有返回值并且可以抛出异常。
	 static class SumTask implements Callable<Long> {
	        @Override
	        public Long call() throws Exception {
	            long sum = 0;
	            for (int i = 0; i < 5; i++) {
	                sum += i;
	                System.out.println("childern: "+i);
	            }
	            System.out.println("sum: "+ sum);
	            return sum;
	        }
	    }

	    public static void main(String[] args) throws ExecutionException, InterruptedException {
	        System.out.println("Start:" + System.nanoTime());
	        
	        //执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
	        FutureTask<Long> futureTask = new FutureTask<Long>(new SumTask());
	        //执行线程方式一:
	        Executor executor=Executors.newSingleThreadExecutor();
	        executor.execute(futureTask);
	        //执行线程执行线程方式二:
	        //new Thread(futureTask).start();
	      
	        //Thread.sleep(10);
	        //取消线程,如果无法取消任务,则返回 false,这通常是由于它已经正常完成;否则返回 true
	        //futureTask.cancel(true);
	        try {
	    	 //取得返回值
		     System.out.println("返回值: "+futureTask.get());
		    // 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
		     System.out.println("返回值: "+futureTask.get(1, TimeUnit.SECONDS));
			} catch (Exception e) {
				e.printStackTrace();
			}
	        
	        //如果在任务正常完成前将其取消,则返回 true。
	        System.out.println("是否被取消: "+futureTask.isCancelled());
	        //如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
	        System.out.println("任务是否完成: "+futureTask.isDone());
	        
	        System.out.println("End:" + System.nanoTime());
	    }
}

运行结果:
在这里插入图片描述

  1. 使用线程池创建线程(企业中常用)(见后续)
  2. 常用线程api
start()start() 启动线程
currentThread()获取当前线程对象
getId()获取当前线程ID Thread-编号 该编号从0开始
getName()获取当前线程名称
sleep(long mill)休眠线程
Stop()停止线程,不安全,不推荐使用
start()start() 启动线程
Thread()分配一个新的 Thread 对象
Thread(String name)分配一个新的 Thread对象,具有指定的 name正如其名。
Thread(Runable r)分配一个新的 Thread 对象
Thread(Runable r, String name)分配一个新的 Thread对象,具有指定的 name正如其名。

代码演示:

public class RunnableDemo {
	public static void main(String[] args) {
		//调用线程
		Thread02 t = new Thread02();
		Thread thread = new Thread(t);
		//启动线程
		thread.start();
		for(int i=0; i<5; i++) {
			System.out.println("main:"+i+"; 线程id: "+Thread.currentThread().getId()+" ;线程name: "+Thread.currentThread().getName());
		}
	}
}

class Thread02 implements Runnable{
	
	@Override
	public void run() {
		for(int i=0; i<5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("childern:"+i+"; 线程id: "+Thread.currentThread().getId()+" ;线程name: "+Thread.currentThread().getName());
		}
	}
}

运行结果:
在这里插入图片描述

三. 守护线程与非守护线程
Java分为两种线程:非守护线程和守护线程
(1)非守护线程(又称用户线程):用户自己创建的线程,如果主线程停止掉,不会影响到到用户线程,和主线程互不影响。
(2)守护线程:指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程。守护线程会和主线程一起销毁。
代码演示:

public class ThreadDemo{

	public static void main(String[] args) {
		//调用线程
		Thread01 thread01 = new Thread01();
		//设置该线程为守护线程,与主线程一起销毁
		thread01.setDaemon(true);
		//启动线程
		thread01.start();
		for(int i=0; i<5; i++) {
			System.out.println("main:"+i);
		}
		System.out.println("主线程执行完毕~~~~~");
	}
}

class Thread01 extends Thread{
	@Override
	public void run() {
		for(int i=0; i<5; i++) {
			System.out.println("childern:"+i);
		}
	}
}

thread01为用户线程时,运行结果:
在这里插入图片描述
thread01为守护线程时,运行结果:
在这里插入图片描述
四. 线程的五种状态
线程的五种状态:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
在这里插入图片描述

(1)新建状态(New):
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码。
(2)就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
(3)运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
(4)阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
(5)死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

五. join、yield和priority方法

  1. join()的作用:是让其他线程变为等待,直到当前线程执行完毕,才释放。
  2. yield()的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)
  3. priority():来控制优先级,范围为1-10,其中10最高,默认值为5。
    代码演示:
public class ThreadJoinDemo {
	//thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
	//比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
	public static void main(String[] args) {

		final Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i=0; i<50; i++) {
					System.out.println("t1: "+ i);
				}	
			}
		});
		// 注意设置了优先级, 不代表每次都一定会被执行。 只是CPU调度会有限分配
		t1.setPriority(10);
		t1.start();
		
		final Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					t1.join();// 让其他线程(t2)变为等待,直到当前t1线程执行完毕,才释放。
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				for(int i=0; i<50; i++) {
					System.out.println("t2: "+ i);
				}
			}
		});
		t2.start();
		
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					t2.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				for(int i=0; i<50; i++) {
					System.out.println("t3: "+ i);
				}	
			}
		});
		t3.start();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值