java多线程

多线程

  • 并行和串行
  • 并行:多个任务同时执行,效率高。
  • 串行:多个任务依次执行,效率低
  • 单核和多核CPU
  • 单核:如:只有一个人帮干活
  • 多核:有多个人帮你干活
  • 线程和进程
  • 进程(Process):正在运行的一个程序称为进程(word 、记事本)
  • 线程(Thread):线程就是进程中的一个任务.
  • 单线程和多线程
  • 如果一个进程中只有一个线程,称为单线程的程序。
  • 如果一个进程中包含两个或者两个以上的线程,称为多线程程序。
  • 进程负责分配内存,多线程共享进程分配的内存
  • 进程的任务之一是向操作系统申请内存
  • 线程向进程申请内存
  • 多线程共享进程分配的内存
  • 线程调度器与调度算法
  • 多个线程谁先执行?线程的运行过程是由线程调度器来负责的。
  • 线程调度器有两个调度算法:时间片轮换调度与抢占式调度

Java对多线程的支持

  • java多线程解决了程序并发执行

创建线程的方式

  • 串行
public class DemoChuanXing {
	public static void main(String[] args) {
		/*
		 * 不使用线程,方法间的调用是串行的
		 * 串行执行时,不能实现多任务并行
		 * 
		 * 串行关键点:
		 * 当a运行时,main等待a运行结束后才能运行main
		 * 当b运行时,a等待b运行结束后才能运行a
		 */
		a();
		System.out.println("main");
	}
	private static void a() {
		b();
		System.out.println("a");	
	}
	private static void b() {
		System.out.println("b");	
	}
}
  • 继承Thread类创建线程
public class MyThread extends Thread{
	/*
	 * 线程并行
	 * 创建线程的方式1、创建类继承Thread类创建线程
	 * (1)创建一个子类继承Thread类,
	 * (2)重写run方法
	 * (3)调用start方法启动线程,启动后会调用run()方法 
	 */
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+"="+i);
		}
	}
}
public class Test {
	public static void main(String[] args) {
		/*
		 * main方法执行时,jvm会创建一个主线程
		 * 
		 */
		MyThread myThread=new MyThread();
		//启动线程,线程会自动调用run方法
		myThread.start();	
	}
}
  • 关于主线程
  • main方法启动时会自动创建一个线程,称为主线程。
  • 子线程需要在主线程中创建。
  • 关于线程的名称
  • Thread.currentThread()获取当前线程
  • Thread.currentThread().getName()获取当前线程的名称
  • 主线程名称默认是main
  • 子线程名称默认是Thread-00 | 01 | 02
  • 修改线程的名称,调用setName方法
  • Thread.currentThread().setName(“主线程”);
  • t.setName(“子线程”);
  • 实现Runnable接口创建子线程
public class MyThread implements Runnable{
	/*
	 * 创建线程2、实现Runnable接口创建线程
	 * 方法(1)定义类,实现Runnable接口
	 *     (2)重写run方法
	 *     (3)将Runnable接口包装到Thread类中
	 *     (4)调用start启动线程
	 *     Runnable是函数式接口
	 * 
	 */

	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+"="+i);
		}
	}
}
public class Test {
	public static void main(String[] args) {
		
		Thread.currentThread().setName("主线程");
		MyThread myThread=new MyThread();
		//将Runnable接口包装到Thread类中
		Thread t=new Thread(myThread);
		t.setName("子线程");
		t.start();
		
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+"="+i);
		}
	}
}

Runnable接口是函数式接口,因此可以使用lambda表达式实现线程。

public class Test1 {
	public static void main(String[] args) {
		/*
		 * Runnable是函数式接口,使用lambda表达式实现线程
		 * 
		 */
		Runnable runnable=()->{
			for(int i=0;i<100;i++) {
				System.out.println(Thread.currentThread().getName()+"="+i);
			}
		};
		new Thread(runnable).start();
		
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+"="+i);
		}
	}
}

实现Callable接口

  • 与Runnable相比,Callable可以有返回值,返回值通过FutureTask进行封装。
ublic class MyCallable implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		return 45;
	}
}

public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		MyCallable myCallable=new MyCallable();
		FutureTask<Integer> f=new FutureTask<>(myCallable);
		Thread thread=new Thread(f);
		thread.start();
		System.out.println(f.get());
	}
}

继承类与实现接口的方式有什么区别

  • java不支持多重继承,因此继承了Thread类就无法继承其他类,但是可以实现多个接口
  • 类可能只要求可执行就行,继承整个Thread类开销过大。

主线程与子线程

  • main方法启动时,JVM自动创建了主线程
  • main方法中new的线程是子线程

线程的基础机制

  1. 让步yield
  • 一个运行中的线程想要放弃执行的权利
  • 此时线程调度器会选择另一个可运行的线程来执行,不过,调度器也可以忽略这个通知
  • yield只是放弃了本时间片的执行,放弃后又回到就绪队列重新排队。
public class ThreadYield {
	public static void main(String[] args) {
		/*
		 * 线程让步yield
		 * 如果一个运行中的线程想要放弃执行的权利,那么可以调用Thread类
		 * 中的静态方法yield来通知线程调度器
		 * 
		 * 本线程要让出对CPU的占用,此时线程调度器会选择另一个可运行的线程来执行,
		 * 不过,调度器也可以忽略这个通知
		 * 注意:yield方法只是临时放弃当前线程的执行,并不代表该线程就不执行了,此时线程将转为就绪状态。
		 */
		Runnable run=()->{
			for(int i=0;i<100;i++) {
				System.out.println(Thread.currentThread().getName()+"="+i);
				Thread.yield();
			}
		};
		new Thread(run).start();
		new Thread(run).start();
	}
}
  1. 休眠sleep
  • 当线程休眠时,它放弃了执行的权利,加入到等待对列,什么都不做,然后等到休眠时间到了再醒过来,加入可运行队列。
public class Sleep {
	public static void main(String[] args) {
		/*
		 * 线程休眠
		 * 与yield方法临时放弃线程执行权利的方式不同,sleep是真正的睡眠。
		 * 
		 * 正常情况下,需要等线程睡眠了指定时间后,才会重新转为可运行状态,被线程调度器所调度。
		 */
		Runnable run=()->{
			while(true) {
				System.out.println(new Date());
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Thread t=new Thread(run);
		t.start();
	}
}

解析:

  • sleep时,线程休眠了,在休眠期间不会进入到就绪队列
  • 当休眠时间结束后,进入就绪队列排队
  • sleep方法会抛出编译时异常InterruptedException(中断)
  1. 优先级priority

Thread类中定义了三个优先级常量

  • MAX_PRIORITY=10
  • NORM_PRIORITY=5
  • MIN_PRIORITY=1

线程优先级

  • 决定了那个线程更容易获取CPU时间片
  • 虽然线程调度器选择哪一个线程运行是由很多因素决定的,但是调度器确实会让优先级高的线程获得CPU
  • 对于两个优先级不同的线程来说,优先级高的线程会得到更多的执行时间。
  • 但是优先级高的线程不一定先执行
public class Priority {
	public static void main(String[] args) {
		Runnable run=()->{
			for(int i=0;i<100;i++) {
				System.out.println(Thread.currentThread().getName()+"="+i);
			}
		};
		Thread t1=new Thread(run);
		Thread t2=new Thread(run);
		//设置优先级别
		t1.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(1);	
		t1.start();
		t2.start();
	}
}
  1. 后台线程(background:后台 daemon:守护)

进程退出是如何界定的?

  • 当一个进程中的所有线程都结束了,这个进程就退出了.当一个进程中只要有一个线程还在运行,那么这个进程就不能退出

为什么要后台线程呢?

  • 后台线程是否在运行,不影响进程的结束。所有的非后台进程都结束了,进程就退出了。

后台线程使用场景

  • 监视程序
  • JVM的垃圾回收线程
	public static void main(String[] args) {
		
		Runnable run=()->{
			while(true) {
				System.out.println(new Date());
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Thread t=new Thread(run);
		//将t设置为守护线程,t是否在运行不影响程序的退出。
		t.setDaemon(true);
		t.start();
	}
  1. 线程间的协作

线程联合join

  • join 方法是让一个线程等待另一个线程执行完毕后再继续执行执行
class A extends Thread{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println(Thread.currentThread().getName()+"="+i);
		}
	}
}
class B extends Thread{
	private A a;
	
	public B(A a) {
		this.a = a;
	}
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			try {
	/*
     * B调用了A的join方法,当a运行结束后,b开始运行
	 * 
	 */
				a.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"="+i);		
		}
	}
}
public class Test {
	public static void main(String[] args) {
		A a=new A();
		B b=new B(a);
		a.setName("牛牛牛牛牛");
		b.setName("*********");
		a.start();
		b.start();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值