15多线程

多任务处理有两种类型:
多进程
多线程

多线程

理解进程和线程
进程:运行中的应用程序称为进程,进程拥有CPU和内存资源。
线程:线程是进程中的一段代码,一个进程可以有多段代码(一个进程包含多个线程),线程本身不拥有资源,共享所有进程的资源。

多进程:在操作系统中能同时运行多个任务
多线程:在同一个应用程序中有多个顺序流(多段代码)在同时执行(同时用QQ聊天,视频)

代码理解:
public static void main(String[] args) {
	method1();
}

public static void method1(){
	System.out.println("进入method1");
	method2();
	System.out.println("退出method1");
}
public static void method2(){
	System.out.println("进入method2");
	System.out.println("method2 execute");
	System.out.println("退出method2");
}

开启这个程序的时候,在操作系统中产生一个进程,程序时main方法开始的,当执行main方法的时候,在当前进程里面开启一个进程(主线程),main调用method1方法,main进入到method1继续执行代码,面里面其他代码无法马上执行,必须等待method1执行完之后再回来执行main的输出语句。

整个程序从开始到结束都只有一个现场,单线程程序,执行路径只有一条。

单线程程序:一个任务完了继续后面任务,不能同时执行,所花时间所有任务加在一起。

进程:
1.把应用程序代码放在内存的代码区,代码放在方法区并没有马上执行,但是这时候已经说明一个进程正在准备开始。进程产生但是没有运行,所以进程是一个静态的概念。
2.进程的执行指的是进程里面的主线程开始执行的时候。main方法的执行,在机器上运行的任务全都是线程。
Windows操作系统是多进程的系统,同时可以执行很多线程,Linux、Unix都是多线程。
Dos是单线程系统。

多线程表示多个任务同时执行,CPU如何分配资源:
CPU的运行速度非常快,一秒钟几亿次,所以将CPU运行时间分成一个一个的小片段,每个任务都可以在一个时间片段里面执行一次,如果在当前片段里面没有执行完,其他任务执行完了继续执行。因为速度很快,给我们的感觉同时在执行
实际上在一个时间节点只有一个任务在执行。

如果你的电脑是双CPU、多核,实现真正多线程。

java是如何创建线程
4种:
1.继承java.lang.thead类,当前类就表示多线程类

 public class T03_MyThread3 extends Thread{
		public T03_MyThread3(String name){
			super(name);
		}
		/**
		 * 线程体,多线程任务都在run里面执行
		 */
		@Override
		public void run() {
			for (int i = 0; i < 1000; i++) {
				//获取到当前线程的名字
				System.out.println(Thread.currentThread().getName()+"----i:"+i);
			}
		}
		public static void main(String[] args) {
			//(1)产生一个线程对象
			T03_MyThread3 t = new T03_MyThread3("线程1");
			//(2) 启动线程
			t.start();
			T03_MyThread3 t2 = new T03_MyThread3("线程2");
			//(2) 启动线程
			t2.start();
		}
	}

2.实现runable接口,可以表示多线程

public class T02_MyThread2 implements Runnable{
	
/**
 * 线程体:线程执行核心内容
 */
	public void run() {    //run方法要一开始就重写
		for (int i = 0; i < 1000; i++) {
			System.out.println("i:"+i);
		}
	}
	public static void main(String[] args) {
		//(创建一个线程对象)
		T02_MyThread2 t2 = new T02_MyThread2();  //线程的执行体(任务)
		Thread th = new Thread(t2);              //产生线程的对象
		
		th.start();
		
		for (int j = 0; j < 1000; j++) {
			System.out.println("=====j:"+j);
		}
	}
}

3.实现callable接口,也可以实现多线程
4.线程池

Thread.currentThead()可以获取当前执行的线程,来获取线程的信息(名字、ID、其他)

启动线程start,为什么调用它而不是run?
api提出start才是启动线程的方法,run线程体,直接调用相当于直接调用方法,产生多线程程序start,将线程放入到线程组(线程队列 先进先出),接下来再调用本地方法start0,通知虚拟机来执行线程,虚拟机默认调用线程的run方法来执行。所以一定执行start方法。

线程优先级的问题
java提供了一个线程调度器来监控程序中启动后进入就绪状态的线程。线程调度器可以根据线程优先级来判断先调度那个线程执行。

线程优先级用我们用1-10来表示,值越大优先级越高,默认缺省值为5(主线程默认为5),程序的运行结果还是由虚拟机来安排,无法预知运行后的结果,线程优先级高低只是一个参考值,相对的结果,理论上是具有高优先权的程序占有更多的CPU使用权,但是有时候并不是这样的结果。

setPriority()设置优先级
getPriority()获取优先级

线程的生命周期
五种状态:
1.新建
2.就绪
3.运行
4.阻塞:等待 同步 其他
5.死亡

*其他阻塞:
sleep()\join()\io操作
线程主动让出执行权:yield()

sleep:Thread.sleep() sleep属于Thread静态方法,单位是毫秒,一旦调用sleep,当前线程处于睡眠状态,当睡眠时间过去后马上恢复到就绪状态。此方法会造成线程阻塞。

join:合并某个线程,如果有两个线程A、B,A必须等B执行完了才能继续往下运行,我们可以调用join来完成这项工作。合并过后就相当于以前直接调用方法

yield:在多线程运行过程中,当前抢到CPU资源的线程想让出使用权,调用yield方法马上就让出使用权变成就绪状态,但是还可能被jvm分配使用权。在实际开发中,无法保证yield让步。

*同步阻塞:

*等待阻塞:

线程的同步

同步:多个线程同时运行,有时候线程之间要共享数据,一个线程需要其他线程的数据,否则就不能保证程序的运行结构。

****并发:同一个时间点,多个线程访问同一资源。
****共享资源(临界资源):多个线程共享数据称为共享资源,或者称为临界资源。

****线程同步:同步就是协同步调,按照预先的先后顺序进行执行,先抢到资源的线程先执行,后抢到资源的线程后执行,两个线程相互配合,共享数据。
****线程互斥:多个线程在共享某个变量,而且都对变量有修改,如果不考虑运行过程中相互协调问题,就会出现数据不一致,数据安全问题。

在java中实现多线程同步:
1.同步块
2.同步方法
3.volatile关键字

***同步块:相当于自动加上内置锁,实现同步操作
语法:

synchronized(){
	//代码只能在同一时间一个线程访问
}

class AirPort{
	private int number = 30;
	public int getNumber(){
		return number;
	}
	//一旦有线程抢到票,余票就要-1
	public synchronized void reduce(){
		
		--number;
		System.out.println(Thread.currentThread().getName()+"抢票成功 余票为:"+number);
		
		//同步要同步,有一个线程在处理余票,另外线程必须等待,当前处理完了还得将余票数据共享出去
		/*synchronized (this) {
			--number;
			System.out.println(Thread.currentThread().getName()+"抢票成功 余票为:"+number);
		}*/
		
	}
}

class TicketThread implements Runnable{
	private AirPort air;
	public TicketThread(AirPort air){
		this.air = air;
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			air.reduce();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

**同步方法:在方法访问修饰符后加上synchronized

public synchronized void add(){}

在java中每个对象都有一个内置锁,当你使用synchronized关键字的时候,内置锁会保护整个方法,在调用方法前需要获取内置锁,否则处于等待状态。

*volatile关键字:给局部变量的访问提供一种免锁机制,一个变量使用它来修饰,会告诉虚拟机该域可能会被其他线程更新,每次使用当前值得时候,都要重新计算一下。默认的值保存在寄存器,所以线程互斥就是直接取寄存器的数据,volatile不会提供任何原子操作,也不能用final来修饰。
private volatile int number = 1;

死锁
死锁:指两个或者两个以上的资源,因争夺资源造成相互等待,若没有外部处理,他们都将无限等待下去。(相互将对象锁起来)

原因:1.系统资源不足
2.进程推进顺序有问题
3.资源分配不当

死锁的案例:
线程1:对a加锁  -- 对b加锁 -- 执行 --  释放b -- 释放a
线程2:对b加锁  -- 对a加锁 -- 执行 --  释放a -- 释放b

线程数据共享
在java中,可以使用wait、notify、notifyall完成线程之间的通信,在开发过程中最常见的例子就是生产者和消费者去消费产品,生产者将产品放入队列里面,消费者就可以从队列里面获取数据,如果没有数据那就等待。

wait 、notify 、notifyall

都属于object对象提供,主要来控制线程状态
****wait:用来将线程置入休眠的一个方法,不会自己恢复,必须等待调用notify、notifyall当前线程才能被唤醒。
调用wait的时候,线程必须要获取带对象的对象级别锁。(wait必须在synchronized锁中间)
一旦调用wait,当前线程就处于休眠状态,当前线程就释放,其他线程可以抢资源。

****notify:唤醒在对象监视下面等待下的单个线程,也就是使用了wait休眠的线程,只能唤醒一个操作本对象的线程。如果有多个线程再次对象上等待,会随机唤醒一个线程,并等待获取对象的对象锁(表示当前就算收到了通知,wait对象也不会马上获取对象锁,notify唤醒的线程执行完了才能获得对象锁)

****notifyall :唤醒当前对象上面所阻塞的所有线程,唤醒的时候顺序是随机的,使用时必须在synchronized锁中间完成

-总结sleep和wait区别
-总结notify和notifyall的区别
-什么时候用sleep,什么时候用wait
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值