day18_java多线程编程

线程

基本概念

进程:内存中正在进行的一个程序

线程:进程中的一个执行流程

多线程:有两个 或者 两个以上并发的执行流程

线程的生命周期,线程分为以下五种状态

  • (新建) New Thread();
  • (就绪) start();
  • (运行) run();
  • (阻塞) 暂停执行
    • sleep(就像我们做总结睡着了),join(让其他人先做,就像做总结时中间让其他人讲话)阻塞

      sleep:等待多少毫秒;超过了之后回复就绪状态等待cpu调用执行
      join:等待其他线程执行完,线程A调用了线程B的join();方法,那么线程A等线程B执行完再执行

    • 同步(排队)

    • wait(晕过去了)

  • (死亡)

线程图解:

这里写图片描述

主线程

当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程(main thread),它是程序开始时就执行的。

主方法就是主线程的任务 main();

public class TestThreadMain {

	public static void main(String[] args) {
		// 获得当前的线程                      主线程名字,优先级,主线程组
		System.out.println(Thread.currentThread());//Thread[main,5,main]
		System.out.println(Thread.currentThread().getName());//main


	}

}

问题:启动一个java应用程序至少启动几个主线程?

会先启动两个线程:一个主线程,一个垃圾回收线程

子线程

默认的名字:Thread —— 数字

创建子线程

  • 方式一:继承自Thread类
  • 方式二:实现Runnable 接口

区别

  1. 继承Thread类,继承了Thread类的所有可以继承的;Runnable接口,只有run();
  2. Runnable接口有利于资源共享

希望主线程最后结束

  1. sleep() 暂停多长时间
  2. isAlive() 判断线程仍在运行,isAlive()方法返回true,如果不是则返回false。
  3. join() 等待一个线程执行完

/** 创建子线程示例代码 */

//创建子线程方法一:继承自Thread类  
class MyThread extends Thread{
	MyThread(String name){
		super(name);
	}

	//子线程的任务功能在run中
	@Override
	public void run() {
		for(int i = 1; i<= 3 ; i++){
			System.out.println(Thread.currentThread().getName() +":"+i);
		}
	}
	
}


//创建子线程方法二:实现Runnable 接口    用的较多,有利于资源共享
class ThreadDemo implements Runnable{

	@Override
	public void run() {
		for(int i = 1; i<= 3 ; i++){
			System.out.println(Thread.currentThread().getName() +"吃饭");
		}
		
	}
}

public class TestThread1 {

	public static void main(String[] args) {
//		//创建一个线程对象
//		Thread t = new Thread();
//		//启动线程
//		t.start();
		
//		//创建一个子线程
//		MyThread t1 = new MyThread("t1");//新建
//		//启动子线程,必须用start,并且只能启动一次
//		t1.start();//就绪
		
		
		//不是线程对象
		ThreadDemo demo = new ThreadDemo();
		//                     绑定ThreadDemo类对象
		//Thread zhangsan = new Thread(demo);
		Thread zhangsan = new Thread(demo,"张三");
		Thread lisi = new Thread(demo,"李四");
		

		//设置优先级
		/*	lisi.setPriority(10);
		zhangsan.setPriority(1);*/
		//最低,普通,最高        比直接赋予数字好一些
		//1   5   10
		zhangsan.setPriority(Thread.MAX_PRIORITY);
		zhangsan.setPriority(Thread.MIN_PRIORITY);
		zhangsan.setPriority(Thread.NORM_PRIORITY);
		
		//启动线程
		zhangsan.start();
		lisi.start();
		
		//希望主线程最后结束方法一
	/*	try {
			Thread.sleep(1000);//等1000毫秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		希望主线程最后结束方法二:判断子线程是否运行
/*		if(zhangsan.isAlive() || lisi.isAlive()){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}*/
		
	//希望主线程最后结束方法三:让子线程都执行完才能恢复主线程
		try {
			zhangsan.join();
			lisi.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("主线程结束");
		

	}

}

线程优先级

1 – 10

10最高

  • 理论上,优先级高的线程比优先级低的线程获得更多的CPU时间
  • 实际上,线程获得的CPU时间通常由包括优先级在内的多个因素决定
  • 理论上,等优先级线程有同等的权利使用CPU
/** 创建子线程 */

//创建子线程方法一:继承自Thread类  
class MyThread extends Thread{
	MyThread(String name){
		super(name);
	}

	//子线程的任务功能在run中
	@Override
	public void run() {
		for(int i = 1; i<= 3 ; i++){
			System.out.println(Thread.currentThread().getName() +":"+i);
		}
	}
	
}

//创建子线程方法二:实现Runnable 接口用的较多,有利于资源共享
class ThreadDemo implements Runnable{

	@Override
	public void run() {
		for(int i = 1; i<= 3 ; i++){
			System.out.println(Thread.currentThread().getName() +"吃饭");
		}
		
	}
}

public class TestThread1 {

	public static void main(String[] args) {
//		//创建一个线程对象
//		Thread t = new Thread();
//		//启动线程
//		t.start();
		
//		//创建一个子线程
//		MyThread t1 = new MyThread("t1");//新建
//		//启动子线程,必须用start,并且只能启动一次
//		t1.start();//就绪
		
		
		//不是线程对象
		ThreadDemo demo = new ThreadDemo();
		//                     绑定ThreadDemo类对象
		//Thread zhangsan = new Thread(demo);
		Thread zhangsan = new Thread(demo,"张三");
		Thread lisi = new Thread(demo,"李四");
		

		//设置优先级
		/*	lisi.setPriority(10);
		zhangsan.setPriority(1);*/
		//最低,普通,最高        比直接赋予数字好一些
		//1   5   10
		zhangsan.setPriority(Thread.MAX_PRIORITY);
		zhangsan.setPriority(Thread.MIN_PRIORITY);
		zhangsan.setPriority(Thread.NORM_PRIORITY);
		
		//启动线程
		zhangsan.start();
		lisi.start();
		
		//希望主线程最后结束方法一
	/*	try {
			Thread.sleep(1000);//等1000毫秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		希望主线程最后结束方法二:判断子线程是否运行
/*		if(zhangsan.isAlive() || lisi.isAlive()){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}*/
		
	希望主线程最后结束方法三:让子线程都执行完才能恢复主线程
		try {
			zhangsan.join();
			lisi.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("主线程结束");
		

	}

}

线程其它方法

interrupt()线程中断 :sleep()和join()

**问题:**java线程什么时候会引发InterruptedException ?

前提:当前线程处于sleep或者join时,被其他线程中断了,那么当前线程会进行异常处理

class ThreadDemo1 implements Runnable{

	@Override
	public void run() {
		for(int i = 1 ; i <=5 ; i++){
			System.out.println(Thread.currentThread().getName()+","+i);
			if(i == 3){	
				try {
					//Thread.sleep(1000);
					Thread.currentThread().join();
				} catch (InterruptedException e) {
					System.out.println("进入异常处理了");
				//	e.printStackTrace();
				}
			}
		}
		
		
	}
	
}

public class TestThreadInterrupt {

	public static void main(String[] args) {
		ThreadDemo1 demo1 = new ThreadDemo1();
		Thread t1 = new Thread(demo1);
		t1.start();
		//主线程调用了子线程t1的interrupt方法,子线程t1被中断进入异常处理
		t1.interrupt();

	}

}

yield() 线程让步 (了解即可,现在基本不用):

Thread.yield():不能完全保证线程让步

class ThreadDemo1 implements Runnable{

	@Override
	public void run() {
		for(int i = 1 ; i <=5 ; i++){
			System.out.println(Thread.currentThread().getName()+","+i);
			if(i == 3){	
				try {
					//Thread.sleep(1000);
					Thread.currentThread().join();
				} catch (InterruptedException e) {
					System.out.println("进入异常处理了");
				//	e.printStackTrace();
				}
			}
		}
		
		
	}
	
}

public class TestThreadInterrupt {

	public static void main(String[] args) {
		ThreadDemo1 demo1 = new ThreadDemo1();
		Thread t1 = new Thread(demo1);
		t1.start();
		//主线程调用了子线程t1的iterrupt方法,子线程t1被中断进入异常处理
		t1.interrupt();

	}

}

线程的同步

什么是同步

某一时刻此资源只能被一个线程独占。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

为什么使用同步

当两个或两个以上的线程需要共享资源,他们需要某种方法来确定资源在某一时刻仅被一个线程占用,达到此目的的过程叫做同步。(某一时刻资源只能被一个线程独占)

使用同步
  1. 同步代码块
    我们可以将对某个方法的调用放入一个synchronized块内
synchronized(对象){ 
 
 同步块; 
 
} 
  1. 同步方法,锁的是对象 this
synchronized  方法{   } 
/**同步示例代码*/
class Bank implements Runnable{
	int money = 0;
	//存钱
	//同步方法
	synchronized public void setMoney(){
		money += 100;//存100
		System.out.println(Thread.currentThread().getName() + "存了100,余额:" +money);
	}
	@Override
	public void run() {
		for(int i = 0 ; i < 3 ; i++){
			//同步锁
			//同步块
/*			synchronized(this){
			setMoney();
			}*/
			setMoney();
		}
		
	}
	
}
public class TestBank {

	public static void main(String[] args) {
		Bank bank = new Bank();
		Thread zhangsan = new Thread(bank,"zhangsan");
		Thread lisi = new Thread(bank,"lisi");
		zhangsan.start();
		lisi.start();
	}

}

Lock

优势

可以显示加锁,释放锁

获得锁:

当一个线程访问此对象的同步块或同步方法的时候,
申请同步锁,申请成功了,就获得了锁。在执行同步块
和同步方法的过程中,其它线程进入线程锁定池中处于
阻塞状态。只有当前执行锁的线程释放了锁其它线程
才有机会获得CPU的调用执行。

释放锁:

1.同步方法或同步块中的代码正常执行完了,就释放了;
2. 出现了未处理的异常Exception和
Error时 ,释放锁;
3. break,return语句,会结束方法或
代码块,那么会释放锁。
4. 执行了 wait()会释放锁。

语法:
try{  
    加锁lock 
    
}finally{   
    释放unLock 
    
}
lock作用
  1. 可以显示加锁和释放锁
  2. 提供了更多功能
死锁
  • 每个对象只有一个锁(lock)不之相关联
  • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制
/**死锁示例代码 */
class Zhangsan{
	public void say(){
		System.out.println("你给我书,我就给你画");
	}
	public void get(){
		System.out.println("张三获得了书");
	}
}

class Lisi{
	public void say(){
		System.out.println("你给我画,我就给你书");
	}
	public void get(){
		System.out.println("李四获得了画");
	}
}
class LockDemo implements Runnable{
	static Zhangsan zhangsan = new Zhangsan();
	static Lisi lisi = new Lisi();
	boolean tag ;
	
	@Override
	public void run() {
		if(tag == true){//张三
			synchronized(zhangsan){
			zhangsan.say();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			synchronized(lisi){
				zhangsan.get();
			}
			}
		}else{//李四
			synchronized(lisi){
				lisi.say();
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				synchronized(zhangsan){
					lisi.get();
				}
			}
		}
		
	}
	
}
public class Testlock {

	public static void main(String[] args) {
		LockDemo demo1 = new LockDemo();
		demo1.tag = true;
		LockDemo demo2 = new LockDemo();
		demo2.tag = false;
		Thread t1 = new Thread(demo1);
		Thread t2 = new Thread(demo2);
		t1.start();
		t2.start();
	}

}

wait()和sleep的区别:

  • 区别:
  • 时间参数
    • wait()可以可定时间也可以不指定; sleep()必须指定时间;
  • 同步状态
    • sleep()释放执行权,不释放锁 ;wait释放执行权,释放锁
class Show implements Runnable{
	int i = 10 ;
	@Override
	synchronized public void run() {
		for(; i >= 0 ; i--){
			
			if(i == 5  && Thread.currentThread().getName().equals("t1:")){
				try {
					//Thread.sleep(100000);
					wait(1000);
					System.out.println("我醒了");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+i);
		}	
		
	}
	
}

public class Exercise1 {

	public static void main(String[] args) {
		Show sh = new Show();
		Thread t1 = new Thread(sh,"t1:");
		Thread t2 = new Thread(sh,"t2:");
		t1.start();
		t2.start();

	}

}

包子铺实例理解wait()、notify()

package day26;
class QingFeng {
	private int count;//包子数量
	private boolean tag = false;//true ->有包子,false->没包子

	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	
	//生产包子
	synchronized public void put(int count){
		//有包子,休息
		if(tag == true){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//没有包子 false
		this.count = count;
		System.out.println("生产了" +this.count);
		tag = true;
		notify();//唤醒销售
	}
	//销售包子
	synchronized public void get(){
		//没包子可卖,休息一会
		if(tag == false){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 有包子true
		System.out.println("卖了" +this.count);
		tag = false;//卖完了
		notify();//唤醒厨师
	}

}
//生产
class Producer implements Runnable{
	QingFeng qingfeng;
	

	public Producer(QingFeng qingfeng) {
		super();
		this.qingfeng = qingfeng;
	}


	public Producer() {
		super();
	}


	@Override
	public void run() {
		//生产
		for(int i = 1 ; i <=5 ; i++){
			qingfeng.put(i);
		}
	}
	
}

//销售
class Consumer implements Runnable{
	QingFeng qingfeng;
	
	public Consumer() {
		super();
	}

	public Consumer(QingFeng qingfeng) {
		super();
		this.qingfeng = qingfeng;
	}

	@Override
	public void run() {
		for(int i = 1 ; i <= 5 ; i++){
			qingfeng.get();
		}
	}
	
}

public class Exercise2 {

	public static void main(String[] args) {
		QingFeng qingfeng = new QingFeng();
		Producer pro = new Producer(qingfeng);
		Consumer con = new Consumer(qingfeng) ;
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		t1.start();
		t2.start();

	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值