多线程

线程

**线程和进程之间的区别:**

进程:每一进程都有自己独立的代码和数据空间, 进程之间切换开销较大,一个进程包含1~n个线 程,进程是资源分配的最小单位
线程:同一类的线程共享代码和数据空间, 线程之间切换开销较小,线程是cpu调度的最小单位

  • 线程和进程一样都分为5个阶段: 创建 就绪 运行 阻塞 终止
  • 多进程: 操作系统能同时运行多个任务(程序)
  • 多线程: 同一个程序中有多个顺序流执行(多任务执行就是多线程),提高效率
  • 单线程:单任务,单个路径执行,需要排序,效率较低

学习多线程的核心掌握知识: 线程的开启方式 线程状态 线程安全

线程的创建方式:
1.继承Thread类,重写run() start()
2.实现Runnable接口,重写run() —推荐
3.实现Callable接口,重写call()

public class ThreadDemo01 extends Thread{
	
	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("一边陪女朋友...");
		}
	}
	
	//主线程
	public static void main(String[] args) {
		//创建一个线程
		ThreadDemo01 th = new ThreadDemo01();
		//开启线程
		th.start(); //等待cpu的调度,时间片同一时刻分配给哪一个线程,哪一个线程就会执行,我们无法控制cpu的调度分配
		
		for(int i=1;i<=20;i++) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("一边敲代码...");
		}
	}
}

开启线程的第二种方式
1.实现Runnable接口,重写run方法

优点:

  •  1)避免单继承的局限性
    
  •  2)实现资源共享
    
public class ThreadDemo02 implements Runnable{

	@Override
	public void run() {
		for(int i=1;i<=20;i++) {
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("一边吃饭...");
		}
	}
	
	
	public static void main(String[] args) {
		
		//1创建线程
		Thread th = new Thread(new ThreadDemo02());
		//2.开启线程
		th.start();
		
		
		for(int i=1;i<=20;i++) {
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("一边讲课...");
		}
	}

}

Web12306

实现Runnable接口,实现资源共享案例(当一份资源多个代理同时操作这是就并发,需要控制安全)

例子:
// 有100张票,3个人分别把这100张票买完

public class Web12306_03 implements Runnable{
	//共享资源  100张票
	int tickets = 100;
	
	public static void main(String[] args) {
		Web12306_03 web = new Web12306_03();
		//创建线程
		Thread th1= new Thread(web,"张三");
		Thread th2= new Thread(web,"李四");
		Thread th3= new Thread(web,"王五");
		
		//开启线程
		th1.start();
		th2.start();
		th3.start();
	}
@Override
	public void run() {
		// A B C
		while(true) {
			if(tickets<=0) {
				break;
			}
			//A B C
			try {
				Thread.sleep(100); //ms数
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
		}
	}
}

//龟兔赛跑
兔子,每跑10步就休息5ms
乌龟一直跑
谁先跑完100步,谁就赢了,如果一旦有人赢了,比赛就结束

public class Racer04 implements Runnable{
	private  String winner;//胜利者
	@Override
	public void run(){
		for(int steps =1;steps<=100;steps++) {		
			if(Thread.currentThread().getName().equals("rabbit") && steps%10==0) {
				try {
					Thread.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"-->"+steps);
			//比赛是否结束
			boolean flag = gameOver(steps);
			if(flag) {
				break;
			}
		}
	}
	/**
	 * 
	 * @param steps  当前参赛者的步数
	 * @return  是否结束游戏
	 * 		true  结束
	 * 		false	继续
	 */
	private boolean gameOver(int steps) {
		if(winner!=null) { //存在胜利者
			return true;
		}else {
			if(steps ==100) {
				winner = Thread.currentThread().getName();
				System.out.println("winner==>"+winner);
				return true;
			}
		}
		return false;
	}
	
	public static void main(String[] args) { 
		Racer04 racer = new Racer04();
		new Thread(racer,"tortoise").start();
		new Thread(racer,"rabbit").start();
	}
}

Callable接口

优点:
call方法可以抛出异常
带出返回值
缺点:
使用麻烦

例子:
//模拟龟兔赛跑,实现Callable接口

public class Racer05 implements Callable<Integer>{
	private  String winner;//胜利者
	
	private boolean gameOver(int steps) {
		if(winner!=null) { //存在胜利者
			return true;
		}else {
			if(steps ==100) {
				winner = Thread.currentThread().getName();
				System.out.println("winner==>"+winner);
				return true;
			}
		}
		return false;
	}

	@Override
	public Integer call() throws Exception {
		for(int steps =1;steps<=100;steps++) {		
			//模拟休息 因为兔子总是睡懒觉,如果是兔子的话,我们就让他每走十步睡2毫秒
			if(Thread.currentThread().getName().equals("pool-1-thread-1") && steps%10==0) {
				try {
					Thread.sleep(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"-->"+steps);
			//比赛是否结束
			boolean flag = gameOver(steps);
			if(flag) {
				return steps;
			}
		}
		return null;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		Racer05 racer = new Racer05();
		//创建执行服务: 
		ExecutorService  ser=Executors.newFixedThreadPool(2);
		//提交执行: 
		Future<Integer> result1 =ser.submit(racer) ;
		Future<Integer> result2 =ser.submit(racer) ;
		//获取结果:  
		Integer r1 =result1.get();
		Integer r2 =result2.get();
		System.out.println(r1+"-->"+r2);
		//关闭服务:  
		ser.shutdownNow();
	}
}

通过内部类实现创建多线程

public class ThreadDemo06 {
	//静态内部类
	static class Inner01 implements Runnable{

		@Override
		public void run() {
			for(int i = 0;i<20;i++) {
				System.out.println("一遍听课..");
			}
		}
		
	}
	
	public static void main(String[] args) {
		//局部内部类
		class Inner02 implements Runnable{
			@Override
			public void run() {
				for(int i = 0;i<20;i++) {
					System.out.println("一遍听歌..");
				}
			}
		}
		
		new Thread(new Inner01()).start();
		new Thread(new Inner02()).start();
		//匿名内部类
		new Thread(new Runnable(){

			@Override
			public void run() {
				for(int i = 0;i<20;i++) {
					System.out.println("一遍吃烧烤..");
				}
			}
			
		}).start();
		//Lambda表达式
		new Thread(()->{
				for(int i = 0;i<20;i++) {
					System.out.println("一遍喝啤酒..");
				}
			}).start();
		
	}
}

线程状态:

新生状态 : new,一个线程一旦进入新生状态,就会有自己的内存和工作空间
就绪状态 : start()方法,线程进入就绪状态,进入就绪不代表会马上执行,会进入就绪队列,等待cpu的调度
运行状态 : 当系统选中某一个正在等待线程,一旦得到获取cpu调度资源,才会运行
阻塞状态 : sleep…
死亡状态|终止状态 : 线程结束

注意:
如果一个线程一旦进入死亡状态,不会恢复,重新创建是一个新的线程了
如果一个线程一旦进入阻塞状态,不会马上恢复运行状态,会先进入就绪,等待cpu的调度

一个线程如何进入终止状态:
1.正常执行完毕
2.stop,destory…(废弃)
3.添加外部标识控制–推荐

如何进入就绪状态:
1).start()
2)阻塞解除
3)调用yield
4)线程切换

如何进入阻塞状态:
1)sleep
2)wait
3)join

sleep: 线程进入睡眠状态,参数为ms数,让出cpu的资源
1.放大问题的可能性
2.模拟网络延迟

//模拟网络延迟倒计时: 倒计时10…9…8…7…1结束

public class StateDemo01 implements Runnable{
	public static void main(String[] args) {
		new Thread(new StateDemo01()).start();
	}

	@Override
	public void run() {
		for(int i=10;i>0;i--) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(i);
		}
	}
}

join 线程插队

public class JoinDemo03 {
	public static void main(String[] args) {
		new Thread(new Father()).start();
	}
}

class Father implements Runnable{

	@Override
	public void run() {
		System.out.println("想吸烟了..");
		System.out.println("给儿子钱,让儿子去买烟..");
		
		//开启儿子线程
		Thread t = new Thread(new Son());
		t.start();
		
//		try {
//			t.join();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//			System.out.println("儿子丢了,去找儿子..");
//		}
		
		System.out.println("接过烟吸一口...");
	}
	
}

class Son implements Runnable{

	@Override
	public void run() {
		System.out.println("借过钱...去买烟");
		System.out.println("路过一家游戏厅,进去玩耍10s");
		for(int i=1;i<=10;i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(i+"s");
		}
		System.out.println("赶紧去买烟...");
		System.out.println("烟买好了给老爸..");
	}
	
}

Thread.State getState() 获取线程状态

public class ThreadState02 {
	public static void main(String[] args) {
		Thread th = new Thread(()->{
			for(int i =1;i<=10;i++) {
				if(i==5) {
					System.out.println("线程即将休眠");
					try {
						Thread.sleep(20);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(i);
			}
		});
		
		System.out.println("th线程状态:"+th.getState()); //NEW
		th.start();
		System.out.println("th线程状态:"+th.getState());  //RUNNABLE
		
		
		while(th.getState()!=Thread.State.TERMINATED) { 
			  System.out.println("th线程状态:"+th.getState()); 
		}
		
		System.out.println("th线程状态:"+th.getState());
	}
}

yield

礼让线程,当线程执行到yield,会让出cpu的资源,直接进入就绪状态

public class YieldDemo02 implements Runnable{
	public static void main(String[] args) {
		YieldDemo02 y = new YieldDemo02();
		new Thread(y,"A").start();
		new Thread(y,"B").start();
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"开始了");
		Thread.yield();
		System.out.println(Thread.currentThread().getName()+"结束了");
	}

}

线程优先级

1~10 默认5
void setPriority(int newPriority)
更改线程的优先级。
int getPriority()
返回线程的优先级
MAX_PRIORITY, MIN_PRIORITY,NORM_PRIORITY

注意: 设置优先级,可以提高优先执行的可能,但不一定
public class PriorityDemo04 implements Runnable{
	public static void main(String[] args) {
		PriorityDemo04 p = new PriorityDemo04();
		Thread th1 = new Thread(p,"zhangsan");
		Thread th2 = new Thread(p,"lisi");
		Thread th3 = new Thread(p,"wangwu");
		Thread th4 = new Thread(p,"zhaoliu");
		
		th1.setPriority(1);
		th2.setPriority(Thread.MAX_PRIORITY);
		th3.setPriority(Thread.NORM_PRIORITY);
		th4.setPriority(7);
		
		th1.start();
		th2.start();
		th3.start();
		th4.start();
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority());
	}

}

当多线程的环境下,多个线程操作同一份资源的时候,可能会发生线程不安全问题

控制线程安全:
synchronized 同步 关键字 锁lock 在同步范围内的代码都需要排队执行
同步方法:在方法上使用synchronized关键字进行修饰
静态方法 -->类
成员方法 -->对象
同步块 :
synchronized(类.class|this|资源){
锁住的代码范围
}
类.class : 每一个类否只有一个class对象,可以通过类名.class获取,唯一的
this : 当前对象
资源 : 成员属性
必须为自定义引用数据类型

锁一定要锁不变的内容,才能锁住
锁的范围大,效率低,锁的范围太小,锁不住

思考: 哪一些代码想要线程排队执行,可以把这些代码控制在同步范围内?锁什么能把这些代码锁住?

public class SynWeb12306_01 implements Runnable{
	//共享资源  100张票
	int tickets = 100;
	
	
	
	public static void main(String[] args) {
		SynWeb12306_01 web = new SynWeb12306_01();
		//创建线程
		Thread th1= new Thread(web,"孙悟空");
		Thread th2= new Thread(web,"猪八戒");
		Thread th3= new Thread(web,"沙悟净");
		
		//开启线程
		th1.start();
		th2.start();
		th3.start();
	}


	//锁范围内的代码都需要排队执行
	// A B C
	@Override
	public void run() {
		while(true) {
			// A B C
			if(!test()) {
				break;
			}
		}
	}
	
	/**
	 * @return false: 票没了
	 * return true: 代表还有票
	 */
	public synchronized boolean test() {
		if(tickets<=0) {
			return false;
		}
		//A B C
		try {
			Thread.sleep(100); //ms数
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
		return true;
	}
}

同步块:
锁类 类名.calss
锁类,这个类的所有对象都被锁住了
可以使用锁this,之锁住当前对象

sleep: 抱着资源睡觉
不会释放对象的锁,会释放cpu的资源

public class SynWeb12306_02 implements Runnable{
	//共享资源  100张票
	int tickets = 100;
	
	
	
	public static void main(String[] args) {
		SynWeb12306_02 web = new SynWeb12306_02();
		//创建线程
		Thread th1= new Thread(web,"孙悟空");
		Thread th2= new Thread(web,"猪八戒");
		Thread th3= new Thread(web,"沙悟净");
		
		//开启线程
		th1.start();
		th2.start();
		th3.start();
	}



	@Override
	public void run() {
		while(true) {
			//A B C
			synchronized (SynWeb12306_02.class) {
				if(tickets<=0) {
					break;
				}
				//A B C
				try {
					Thread.sleep(100); //ms数
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
			}
		}
	}
}

生产者消费者模式
通过wait和notify方法实现线程通信,必须使用在一个同步唤醒下
wait 当一个线程进执行到wait方法,进入到一个和该对象相关的等待池中进行等待,等待对方唤醒
同时释放对象锁,和cpu的资源
notidy: 唤醒对象等待池中的线程,被唤醒的线程只是被激活了,需要再次获取对象的资源才能执行
wait,notify,notifyAll方法用于协调多线程对共享数据的处理,所以只能在一个同步环境下使用

sleep: 不会释放对象资源,释放cpu资源
wait: 会释放对象资源,释放cpu资源

public class Demo004Synchronized4 {
	public static void main(String[] args) {
		SyncStack stack = new SyncStack();
		ShengChan sc = new ShengChan(stack);
		Chi chi = new Chi(stack);
		
		sc.start();
		chi.start();
	}
}

// 馒头类
class Mantou {

}

// 有生产、有消费功能的工厂
class SyncStack{
	List<Mantou> list = new ArrayList<Mantou>();  // 容器,10就满了
	
	// 生产馒头的方法
	public synchronized void push(Mantou mantou){
		
		// 我认为现在已经放满了
		if(list.size()==10){
			try {
				// 通知别人可以来买了
				this.notify();//如果不唤醒的话。以后这两个线程都会进入等待线程,没有人唤醒。
				// 满了之后就停止生产
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else{
			list.add(mantou);
			System.out.println("生产第" + +list.size()+ "个馒头");
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	// 吃馒头的方法
	public synchronized void pop(){
		// 吃完了
		if(list.size()==0){
			try {
				// 通知别人该生产了
				this.notify();; //唤醒在当前对象等待池中等待的第一个线程。notifyAll叫醒所有在当前对象等待池中等待的所有线程。
				// 停下吃的动作
				this.wait();
                //wait后,线程会将持有的锁释放。sleep是即使睡着也持有互斥锁。
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		list.remove(list.size()-1);
		System.out.println("现在还剩下  " + list.size() + " 个馒头");
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

// 生产馒头的线程
class ShengChan extends Thread{
	SyncStack stack;
	public ShengChan(SyncStack stack){
		this.stack = stack;
	}
	
	@Override
	public void run() {
		// 一直生产馒头
		while(true){
			Mantou mantou = new Mantou();
			stack.push(mantou);
		}
	}
}
class Chi extends Thread{
	SyncStack stack;
	public Chi(SyncStack stack){
		this.stack = stack;
	}
	
	@Override
	public void run() {
		// 一直吃馒头
		while(true){
			this.stack.pop();
		}
	}
}

例子
//人车公用街道
一个街道 ,有一个信号灯, 提供2个功能 东西走向的功能we 一个南北走向的功能ns
人 走东西 true
车 走南北 false

public class StreetDemo05 {
	public static void main(String[] args) {
		Street s = new Street();
		new Thread(new Person(s)).start();
		new Thread(new Car(s)).start();
	}
}

class Street{
	boolean flag = false;
	
	//南北
	synchronized void ns() {
		if(flag==true) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("车走....");
			//唤醒对方
			this.notify();
			//车走完了,信号灯要改为true
			flag=true;
		}
		
	}
	
	//东西
	synchronized void we() {
		if(flag==false) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("人走....");
			//唤醒对方
			this.notify();
			//车走完了,信号灯要改为true
			flag=false;
		}
	}
}

class Person implements Runnable{
	Street street;
	
	public Person(Street street) {
		this.street = street;
	}

	@Override
	public void run() {
		while(true) {
			street.we();
		}
	}
}

class Car implements Runnable{
	Street street;
	
	public Car(Street street) {
		this.street = street;
	}
	
	@Override
	public void run() {
		while(true) {
			street.ns();
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值