14、多线程

多线程

程序:Program,是一个指令的集合

进程:Process,(正在执行中的程序)

​ 进程是程序的一次动态执行过程,占用特定的地址空间。

​ 每个进程都是独立的,由三部分组成cpu,data,code

​ 缺点:内存的浪费,cpu的负担

线程:是进程中一个“单一的连续控制流程”(a singles Thread,equential flow of control)/执行路径

​ 1.线程又被称为轻量级进程(lightweight process)

​ 2.Thread run at the same time, independently of one another

​ 3.一个进程中的线程共享相同的内存单元/内存地址空间->可以访问相同的变量和对象,而且他们从同一堆中分配对象->通信. 数据交换 同步操作

​ 4.由于线程间是在同一地址空间上进行的,所以不需要额外的通信机制,这使得通信更加简便而且信息传递的速度也更快。

线程与进程

进程与线程

创建线程的两种方式

通过继承Thread类来创建

缺点:Java是单继承的,如果一个类继承了Thread类,那么该类就不法继承其他的类,所以不建议使用这种方式。并且成员变量都是特有的不具备共享。

/*在 Java 中负责线程的这个功能的是 Java.lang.Thread 这个类 

可以通过创建 Thread 的实例来创建新的线程。 

每个线程都是通过某个特定 Thread 对象所对应的方法 run( )来完成其操作的,方法 

run( )称为线程体。 

通过调用 Thead 类的 start()方法来启动一个线程。
*/
public class TestThread extends Thread {
	public void run() {
		for(int i=0;i<100;i++){
		System.out.println(this.getName()+":"+i);
		} 
	}
	public static void main(String[] args) {
		TestThread thread1 = new TestThread();
		thread1.start();
		TestThread thread2 = new TestThread();
		thread2.start();
	}
}

实现Runnable接口

Runnable接口里面有一个方法。run();

实现Runnable接口的实现类不具备创建线程的能力,所以创建线程需要用Thread的构造方法,里面可以传进一个Runnable接口的实现类。再调用start();

优点:避免了单继承的问题,接口是可以实现多继承的,并且多个线程还可以共享资源。

缺点:写一个线程需要写大量的代码。

package thread;

public class MyThread02 implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			System.out.println("thread02:"+i);
		}
	}
}
///////////////////////////////////////
package thread;

public class Test02 {
	public static void main(String[] args) {
		MyThread02 mt02 = new MyThread02();
		Thread th = new Thread(mt02);
		th.start();
		for (int i = 0; i < 50; i++) {
			System.out.println("main:"+i);
		}
	}
}

比如说网上购票系统。票数是固定的,多个窗口出售的只能是这么多票,而不是每个窗口都有这么多张票。

但是资源共享的情况下,一张票也可能会被多个抢到,所以这里涉及到了线程安全的问题。

代理设计模式

什么称为代理设计模式?

当你需要做某种东西,而你又做不了,那你又不得不去做,所以需要去找一个会做这个东西的人或物来帮你做。

而具有某种能力的东西,我们在代码里面就是接口,接口里面的方法就是一种能力。

所以将接口定义成自己的成员变量,而帮你做事情的这个人则必须实现该接口才能帮你去做这个事情,达成协议后,当你需要做这件事的时候,你只需要通过成员变量调用接口里的方法即可。这种设计模式称为代理设计模式。

//接口
package driver;

public interface DrivingInterface {
	void driving();
}
//需要找代理
package driver;

public class Person {
	private DrivingInterface dr;//将他变为成员变量
	
	public void goHome() {
		dr.driving();//调用方法来找到代理
	}
	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Person(DrivingInterface dr) {
		super();
		this.dr = dr;
	}

	public DrivingInterface getDr() {
		return dr;
	}

	public void setDr(DrivingInterface dr) {
		this.dr = dr;
	}	
}
//受代理
package driver;

public class Driver implements DrivingInterface{

	@Override
	public void driving() {
		System.out.println("代驾开车了......");
	}

}
//测试
package driver;

public class Test {
	public static void main(String[] args) {
		Person wangcai = new Person();
		Driver dr = new Driver();
		wangcai.setDr(dr);
		wangcai.goHome();
	}
}

多线程的生命周期

线程的生命周期

新生状态:当线程被new出来的时候。(无资格无资源)

就绪状态:当线程调用start()方法后就进入就绪状态。(有资格无资源)

运行状态:当调到cpu的资源后,进入的运行状态,当run()还没执行完就结束了,就会回到就绪状态。(有资源有资格)

阻塞状态:导致阻塞线程的事件,比如线程的休眠sleep方法(无资格让资源)

死亡状态:run()正在的执行完毕,抛出未捕获的异常。

常用的方法:

​ currentThread();获得当前运行的线程。

​ getName();获取当前线程的名字。

​ setPriority();设置优先级。最高为10.

​ sleep();让线程进入休眠。

​ join();调用的该线程要执行完才能执行其他的线程。

​ yield();让出一次cpu的资源,然后再与其他线程一起抢。

​ interrupter();中断休眠。

package practect;


class MyThread extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName()+"-------"+i);
//			try {
//				Thread.sleep(5000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
		
		}
	}
}
/////////////////////////////////////////////////////////////////////////
public class Text {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		mt.start();
		//设置线程优先级
	//	mt.setPriority(10);
//		StackTraceElement[] st = mt.getStackTrace();
//		for (StackTraceElement str : st) {
//			System.out.println(str);
//		}
	//	Thread.currentThread().setPriority(10);
		for (int i = 0; i < 50; i++) {
			//currentThread()获取正在运行的线程   getName()获取线程的名字
			System.out.println(Thread.currentThread().getName()+"****"+i);
			//System.out.println(mt.getState());
//			if(i==49) {
				//判断线程是否存活,从线程调用start()方法开始,就绪状态到死亡状态为true
//				System.out.println(mt.isAlive());
//				
//				try {
			//sleep()让线程进入休眠
//					Thread.sleep(1000);
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				//中断正在休眠的线程
//				mt.interrupt();
//				try {
//					//执行到这里,其他线程会进入阻塞状态,
//					//等被执行的线程完成执行完再重新回到join方法后执行其他的
//					mt.join();
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
////			}
			//让出cpu的资源,然后再重新一起抢夺
			if(i==20) {
				Thread.yield();
			}
		}
	}
}

同步锁synchronized

当我们线程需要处理数据的时候,会导致数据的不一致,此时会涉及到线程的安全问题,比如说同一张票多个人抢,当一个线程判断还有一张票,cpu资源就用完了。只能能下一次调用到下一次的cpu完成下面的操作,此时票依然还是有一张,而其他线程得到cpu的资源有开始判断,而if仍然成立,这就会造成两个或者多个人抢到的是同一张票,这一样就会造成线程的安全问题。

为了解决这一问题,引入了同步锁的概念。当有了同步锁,当执行锁定的数据时,这个数据没有用完,锁就不解,而其他的线程只能处于等待状态,等什么时候锁解了,其他线程才会去抢占这个资源,而抢到的这个线程又会将这个数据锁住,这样就解决了数据不一致的问题。

缺点:会浪费大部分的时间来等待资源。

实现同步有两种方式:

一是同步代码:synchronized(this){//需要同步的代码}

二是同步方法:public synchronized void

(){//需要同步的代码}//在方法返回值前面加synchronized修饰符

package demo;

public class Account implements Runnable{
	private int money = 1000;
	private boolean flag = false;
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
=======》	synchronized(this) {
				if(money>=0) {
						System.out.println(Thread.currentThread().getName()+",准备取款");
						try {
							Thread.sleep(200);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
						if(money-200>=0) {
							System.out.println("取出200元");
							System.out.println("剩余:"+(money-=200)+"元");
							System.out.println(Thread.currentThread().getName()+",完成取款");	
						}else {
							System.out.println("余额不足,"+money+"元"+Thread.currentThread().getName()+"的取款,不能完成支付");
						}	
				}else {
					System.out.println("余额不足,"+money+"元"+Thread.currentThread().getName()+"的取款,不能完成支付");
				}
			}
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	
	}
	public void qu(int money) {
		
	}
}

死锁

双方都进入互相等待的逻辑时。

死锁

等待和唤醒

生产者/消费者 模式

我们都知道需要有生产者生产出来东西才能被消费者去使用,同时,生产多少,才能消费多少,而不是你没有生产我也可以去消费,也不能我消费的比生产的还有多,所以为了解决这问题,我们加上了等待和唤醒。、

wait();线程等待

notifyAll();线程唤醒

当我们消费者的线程执行到的时候,是要先进入到判断生产者是否生产出东西了,你才能使用,不然你就只能进入到等待状态。要有等待唤醒,或者设置等待的时间已过,否则将不能被是使用

方法名 作用
final void wait() 表示线程一直等待,直到其它线程通知
final void wait(long timeout) 线程等待指定毫秒参数的时间
final void wait(long timeout,int nanos) 线程等待指定毫秒、微妙的时间
final void notify() 唤醒一个处于等待状态的线程
final void notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行

生产者消费者

//生产的商品
package demo03;

public class Goods {
	/**
	 * 当flag为false的时候,让生产者去生产,让消费者去等待
	 * 当flag为true的时候,让生产者等待,让消费者去消费
	 */
	private boolean flag = false;
	private String name;
	private String brand;
	public Goods() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Goods(String name, String brand) {
		super();
		this.name = name;
		this.brand = brand;
	}
	
	public synchronized void cuts() {
		//一开始生产者没有生产,所以flag为false,所以要取反
		if(!flag) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("消费者消费:"+getBrand()+getName());
		flag = false;
		notifyAll();
	}
	public synchronized void prdc(int i) {
		//如果有商品就进入等待,所以不用取反
		if(flag) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
			if(i%2==0) {
				setBrand("旺仔");
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				setName("小馒头");
				System.out.println("生产者生产:"+getBrand()+getName());
			}else {
					setBrand("哇哈哈");
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					setName("矿泉水");
					System.out.println("生产者生产:"+getBrand()+getName());
			}
			//表示生产好商品了
			flag = true;
			notifyAll();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	
}
//生产者
package demo03;

public class Prd extends Thread{
	private Goods gs;
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			gs.prdc(i);
		}
	}
	public Prd(Goods gs) {
		super();
		this.gs = gs;
	}
	public Prd() {
	}
	public Goods getGs() {
		return gs;
	}
	public void setGs(Goods gs) {
		this.gs = gs;
	}
	
	
	
}
//消费者
package demo03;

public class Customer extends Thread{
	private Goods gs;
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			gs.cuts();
		}
	}
	public Customer(Goods gs) {
		super();
		this.gs = gs;
	}
	public Customer() {
	}
	public Goods getGs() {
		return gs;
	}
	public void setGs(Goods gs) {
		this.gs = gs;
	}
	
}
//测试类
package demo03;

public class Test {
	public static void main(String[] args) {
		Goods gs = new Goods();
		new Prd(gs).start();
		new Customer(gs).start();
	}
}

posted @ 2022-03-17 20:05  站着说话不腰疼  阅读(55)  评论(0)    收藏  举报