Day26-多线程

多线程

线程的基本状态

1、初始(新生)状态

2、就绪状态

3、运行状态

4、终止(死亡)状态

常见方法

休眠sleep

/**
 * sleep:
 * 休眠
 * 使线程停止运行一段时间,将属于阻塞状态
 * 如果调用sleep方法之后,没有其他等待执行的线程,当前线程不会马上恢复执行
 * 预备
 * 3、2、1、Go!
 */
public class TestSleep1 {
	public static void main(String[] args) {
		System.out.println("预备...");
		for (int i = 3; i > 0; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);//毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Go!");
	}
}

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 模拟时间打印
 */
public class TestSleep2 {
	public static void main(String[] args) {
		DateFormat df = new SimpleDateFormat("HH:mm:ss");
		while (true) {
			Date date = new Date();
			String s = df.format(date);
			System.out.println(s);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

放弃yield

/**
 * yield
 * 放弃
 * 当前线程放弃时间片,回到就绪状态,竞争下一次时间片
 * 如果调用yield方法之后,如果没有其他线程等待,当前线程会马上恢复运行
 */
public class TestYield {
	public static void main(String[] args) {
		FirstThread ft = new FirstThread();
		SecondThread st = new SecondThread();
		ft.start();
		st.start();
	}
		
}

class FirstThread extends Thread{
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("1Thread");
			Thread.yield();
		}
	}
		
	}
class SecondThread extends Thread{
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("2Thread");
			Thread.yield();
			
		}
	}
}

加入join

/**
 * join
 * 加入
 * 允许其他线程加入到当前线程中。当前线程会阻塞,直到加入线程执行完毕
 * 
 * 【注】在start方法之后调用
 */
public class TestJoin extends Thread{
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.getName());
		}
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 20; i++) {
			System.out.println("main...");
			if (i == 5) {
				//插入线程
				TestJoin tJoin = new TestJoin();
				tJoin.setName("程咬金线程出现了!");
				tJoin.start();
				try {
					tJoin.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

优先级

/**
 * 乌龟线程
 */
public class TortoiseThread extends Thread{

	@Override
	public void run() {
		
		while (true) {
			System.out.println("乌龟领先了!"+"线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
		}
	}
	
}

/**
 * 1、如何查看线程优先级
 * getPriority()
 * 2、如何修改优先级
 * setPriority()
 * 【注】在start之前设置
 * 3、线程优先级
 * 最小优先级1
 * 默认优先级5
 * 最大优先级10
 */
public class TestThread {
	public static void main(String[] args) {
		//乌龟线程
		TortoiseThread tortoiseThread = new TortoiseThread();
		tortoiseThread.setName("王八线程");
		tortoiseThread.setPriority(9);
		tortoiseThread.start();
		//兔子线程
		Thread.currentThread().setName("兔子线程");
		Thread.currentThread().setPriority(1);
		while (true) {
			System.out.println("兔子领先了!"+"线程名:"+Thread.currentThread().getName()+",优先级:"+Thread.currentThread().getPriority());
		}
	}
	
}

守护线程

/**
 * 守护线程 
 * 线程中有两类:用户线程(前台线程)、守护线程(后台线程)
 * 如果程序中所有前台线程都执行完毕了,后台线程会自动结束
 * 例如:垃圾回收器 gc,就是一种守护线程
 * setDaemon(true)设置为守护线程
 * 
 */
public class TestDaemon extends Thread{

	@Override
	public void run() {
		while (true) {
			System.out.println(this.getName());
		}
	}
	
	public static void main(String[] args) {
		//创建子线程
		TestDaemon td = new TestDaemon();
		//设置为守护线程 不设置守护线程会一直运行 设置后,主线程运行完,守护线程结束
		td.setDaemon(true);
		td.start();
		
		for (int i = 0; i < 100; i++) {
			System.out.println("main...");
		}
	}
}

优化昨日12306售票

/**
 * 1、利用同步代码块完成线程同步操作
 * synchronized (临界资源对象){
 * 	//对临界资源对象加锁
 * }
 */
public class TicketRunnable implements Runnable{
	private int tickNum = 1000;
	@Override
	public void run() {
		while (true) {
			//参数,临界资源对象,引用数据类型
			synchronized (this) {
				if (tickNum <= 0) {
					break;
				}else {
					//售票
					System.out.println(Thread.currentThread().getName()+"窗口,"+"票:"+tickNum);
					
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					//票数-1
					tickNum--;
				}
			}
		}
	}

}

/**
 * 模拟12306售票
 * 线程同步优点:保障安全  缺点:牺牲性能
 */
public class TestTicket {
	public static void main(String[] args) throws Exception {
		//创建四个窗口
		TicketRunnable tr = new TicketRunnable();
		Thread t1 = new Thread(tr,"1号");
		Thread t2 = new Thread(tr,"2号");
		Thread t3 = new Thread(tr,"3号");
		Thread t4 = new Thread(tr,"4号");
		
		//启动线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
		//join方法
		t1.join();
		t2.join();
		t3.join();
		t4.join();
		System.out.println("程序结束");
	}
}

/**
 *  2、利用同步方法完成线程同步操作
 * synchronized 返回值名称 方法名(形参列表){
 * 	
 * }
 */
public class TicketRunnable implements Runnable{
	private int tickNum = 1000;
	@Override
	public void run() {
		while (true) {
			if (sellOne()) {
				break;
			}
		}
	}
	public synchronized boolean sellOne() {
		if (tickNum <= 0) {
			return true;//票卖完了
		}else {
			//售票
			System.out.println(Thread.currentThread().getName()+"窗口,"+"票:"+tickNum);
			
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			//票数-1
			tickNum--;
			return false;
		}
	}

}

死锁

死锁产生的原因

同步嵌套,同步中还有同步,两个同步不是同一把锁

死锁出现的四个条件

互斥条件:资源不能被共享,只能被一个线程使用。

请求与保持条件:已经得到线程资源还可申请新的资源。

非剥夺条件:已经被分配资源的锁,不能在相应的线程强行剥夺。

循环等待:在系统中若干循环形成环路,导致环路中的每个线程都等待相邻线程释放资源。

生产者消费者(等待唤醒机制的经典案例)

/**
 *如果是if判断
 *  生成产品未消耗就又生成了
 *分析原因:
 *  A.本方唤醒本方
 *  B.被唤醒的本方没有生成标识符继续生成
 *    解决方案:
 *         1.把if修改为while
 *          出现死锁现象
 *          死锁现象的原因:本方唤醒本方,被唤醒的本方继续判断标识符,继续等待,一直等待
 *          解决方案:
 *          notify() 换成  notifyAll();//唤醒所有
 */
public class ProCusPractice {
	public static void main(String[] args) {
		//创建对象
		Source source = new Source();
		Producer2 producer2 = new Producer2(source);
		Customer2 customer2 = new Customer2(source);
		
		//创建线程
		Thread t1 = new Thread(producer2);//生成者线程
		Thread t2 = new Thread(customer2);//消费者线程
		Thread t3=new Thread(producer2);//生成者线程
		Thread t4=new Thread(producer2);//消费者线程

		//启动
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

/**
 * 资源类
 * 重点:生产完通知消费者消费
 *       消费完通知生成者生产
 */
class Source{
	//资源名
	private String name;
	//判断标志 标志位:代表有没有资源
	private boolean flag;
	
	public synchronized void set(String name) {
		while(flag) {//true代表有资源,有资源就不生产 生产者进行等待
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
		//没有资源就生成
		this.name = name;
		//生成完资源改一下标志位,有商品了
		flag = true;
		System.out.println(Thread.currentThread().getName()+"生产了");
		//生产完了唤醒消费者
		notifyAll();
	}
	
	public synchronized void get() {
		System.out.println("消费了:"+this.name);
		while(!flag) {
			//没有资源,消费者等待生产
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"消费了");
		//改一下标志位 消费完没有资源了
		flag = false;
		//消费完后 唤醒生产者
		notifyAll();
	}
}

/**
 * 生产者线程
 * 生产!!!!
 */
class Producer2 implements Runnable{
	//生产者生产
	private Source source;
	
	public Producer2(Source source) {
		this.source = source;
	}
	
	@Override
	public void run() {
		while (true) {
			//生产产品
			source.set("面包");
		}
	}
}

/**
 * 消费者线程
 * 消费!!!!
 */
class Customer2 implements Runnable{
	//消费者消费
	private Source source;
	
	public Customer2(Source source) {
		this.source = source;
	}
	
	@Override
	public void run() {
		while (true) {
			//消费产品
			source.get();
		}
	}
	
}

高级多线程

/**
 * 常用的线程池接口和类(所在包java.util.concurrent):
 * Executor:线程池的顶级接口。
		ExecutorService:线程池接口,可通过submit(Runnable task) 提交任务代码。
		Executors工厂类:通过此类可以获得一个线程池。
		通过 newFixedThreadPool(int nThreads)  获取固定数量的线程池。参数:指定线程池中线程的数量。
		通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,无上限。
		newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
        newScheduledThreadPool(int corePoolSize)  创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
 */
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	public static void main(String[] args) {
		//1、创建一个线程 定长线程池newFixedThreadPool
		ExecutorService es = Executors.newFixedThreadPool(4);
		//1.创建一个线程 缓冲线程池 线程个数由任务决定 newCachedThreadPool
		//ExecutorService es = Executors.newCachedThreadPool();
		//1.创建一个线程 单线程 线程池 newSingleThreadExecutor
		//ExecutorService es =Executors.newSingleThreadExecutor();
		//1.创建一个线程 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。newScheduledThreadPool
		//ExecutorService es = Executors.newScheduledThreadPool(4);
		
		//2、任务
		Runnable runnable = new Runnable() {
			private int ticNum=100;
			@Override
			public void run() {
				while (true) {
					synchronized (this) {
						if (ticNum <= 0) {
							break;
						}
						System.out.println(Thread.currentThread().getName()+"窗口卖了"+ticNum+"张票。");
						ticNum--;
					}
				}
			}
		};
		
		//3、提交任务
		for (int i = 0; i < 4; i++) {
			es.submit(runnable);
		}
		
		//4、关闭线程池
		es.shutdown();//等待 所有任务都执行完,然后关闭
//		es.shutdownNow()//立即关闭 可能导致任务执行失败
//		boolean isTerminated()如果关闭后所有任务已完成,则返回true
		while (!es.isTerminated()) {
			
		}
		System.out.println("程序结束");
	}
}
posted @ 2021-08-06 21:20  CN_Darren  阅读(61)  评论(0)    收藏  举报