【Java9】异常,finally,线程创建(卖票),线程同步(卖包子),线程练习

文章目录

  • 1.错误和异常区别:Arrays.toString(array)
  • 2.编译和运行异常:SimpleDateFormat
  • 3.处理异常:方法声明抛出
  • 4.finally关键字:catch相当于else if,finally相当于else,return
  • 5.自定义异常:extends
  • 6.练习:常见异常
  • 7.线程两种创建方式:new Thread(new Runnable() {}),extends Thread,implements Runable
  • 8.卖票:原子性
  • 9.线程同步:synchronized关键字/方法,Lock接口,ThreadLocal
  • 10.卖包子:wait,notify
  • 11.在子线程中输出1-100之间的偶数,主线程输出1-100之间的奇数
  • 12.创建和启动2个子线程:一个打印1-10之间奇数,一个打印1-10之间偶数
  • 13.创建和启动2个子线程:一个打印奇数,一个打印偶数
  • 14.使用两个线程循环打印出1~100
  • 15.使用三个线程循环打印出1~100
  • 16.账户类:synchronized 方法
  • 17.模拟多个人通过一个山洞:synchronized 方法


1.错误和异常区别:Arrays.toString(array)

package com.itheima01.throwable;
import java.util.Arrays;
//错误(Error) : 从程序角度 只能避开,不能解决。 异常(Exception) : 从程序角度 可以解决的问题
public class ThrowableDemo {public static void main(String[] args) {   // int[] array = new int[3];//System.out.println(array); //[I@6d6f6e28//System.out.println(array.toString()); //[I@6d6f6e28//System.out.println(Arrays.toString(array)); //[0, 0, 0]   // int[] array = new int[2_000_000_000]; //20亿撑不住,内存条不够
// System.out.println(Arrays.toString(array)); //java.lang.OutOfMemoryError: Java heap(堆) space //OOM :内存溢出【错误】int[] array = {};System.out.println(array[1]); //java.lang.ArrayIndexOutOfBoundsException【异常】}
}

在这里插入图片描述

package com.itheima02.jvm;
import java.util.ArrayList;
/*
*  throw 关键字(全小写): 1. 效果等同于return,后续代码不再执行
*       2. throw + Throwable对象(只能跟异常对象);  return 返回值;
*       3. 运用: 我们一般不使用, JDK方法的设计者使用 (抛出异常: api设计者 和 使用者交流方式)
*/
public class ThrowDemo {public static void main(String[] args) {
//        new ArrayList(-1); //IllegalArgumentExceptionint[] array = {0,1};int element = getElement(array);System.out.println(element);}private static int getElement(int[] array) {int index = 2;if(index > array.length - 1){ //>2-1//访问了数组不存在的索引
//          ArrayIndexOutOfBoundsException e = new ArrayIndexOutOfBoundsException("you are a stupid bird,you access a wrong index:" + index);
//          throw e; //抛出异常,下面代码不会执行。抛出去之后也没人处理	throw  new ArrayIndexOutOfBoundsException(index); //这一行等同上两行            //throw new Throwable(""); //也可以,因为是编译异常,声明getElement方法后要加throws Throwable}int element = array[index]; //上面有throw,这里两行都不执行return element;}
}

2.编译和运行异常:SimpleDateFormat

在这里插入图片描述

public class ExceptionDemo {public static void main(String[] args) throws IOException {String str = "1996-01-01";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");System.out.println(sdf); //java.text.SimpleDateFormat@f67a0200//  sdf.parse(str); //解析异常ParseException也叫编译异常,和IOException并列关系,main声明需抛出。Date parse = sdf.parse(str); //加上Date parse不报错System.out.println(parse); //Mon Jan 01 00:00:00 CST 1996//   FileOutputStream fos = new FileOutputStream(""); //FileNotFoundException是IOException子类
//   fos.write(97); //IOException是最经典的编译异常//111111111111111111111111111111111111111111111以下都为RuntimeException的子类
//       1.  NullPointerException  空指针String s = null;
//        System.out.println(s.length());// 		2. IndexOutOfBoundsException  索引越界int[] array = {};
//        System.out.println(array[1]);// 3. ClassCastException  类转换// 4. IllegalArgumentException 非法参数new ArrayList<String>(-1);}
}

3.处理异常:方法声明抛出

在这里插入图片描述

package com.itheima03.throwsd;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class ThrowsDemo {public static void main(String[] args) throws IOException {method01(); //表哥又抛出去叫大哥(jvm,因为main方法调用者是jvm),jvm出错又打印终止} /**  throws 关键字*    1. 用在方法声明上: 修饰符 返回值类型 方法名(参数列表) throws 异常类型(A)*    2. 这是处理异常的一种方式: 声明将异常抛出给调用者处理(假手与人)*    3. 注意: 这里的异常类型A 必须跟方法里要抛出的异常B一致, 或者A是B的父类 (向上转型)* *    语法: throws 异常1,异常2{ }*    运用: 我们可以在某一方法的设计上先声明抛出异常,可以方法的调用处进行处理*    切记有有一环节必须处理, 不然到JVM中, 出现异常就崩溃。如果明知不会错的异常,直接throws。*/private static void method01() throws IOException { //交给表哥 FileOutputStream fos = new FileOutputStream("a.txt");fos.write(97);}
}
package com.itheima04.trycatch;public class TrycatchDemo {public static void main(String[] args) {int[] array = {0,1};try{int element = array[2];System.out.println(element);}catch (Exception e){//catch关键字监视try中代码块,我们程序自己把异常处理了,所以不会传给JVM,程序不会终止。e.printStackTrace();//打印异常信息: 打印栈中追溯,案发地点在方法栈中。jvm不会打印了,我们自己手动打印。这行注释了,下面红字不会打印,但“出现异常..继续运行...”都会打印出。System.out.println("出现异常,并被捕获了");}System.out.println("程序继续执行");}
}

在这里插入图片描述

package com.itheima04.trycatch;
import java.util.Scanner;
/*
*   try...catch运用: 1. 如果代码都是由我们自己编写的,我们一般都不会出现异常
*        2. 有些代码需要用户参与编写 (交互: 我们程序输出,用户输入)
*思想: 我们预知代码可能运行结果(如果了包含了异常, 提前try...cath并给出提示或者解决方案)
*/
public class TryCatchDemo02 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);System.out.println("请输入一个被除数:");int a = sc.nextInt();        System.out.println("请输入一个除数:");int b = sc.nextInt();        try {            int result = a/b; //java.lang.ArithmeticException: / by zero  算术异常System.out.println(result);}catch (Exception e){System.out.println("你个傻鸟,除数不能为0");}System.out.println("软件继续让你玩");}
}
package com.itheima04.trycatch;
/*try{}catch (NullPointerException e){//1. 如果出现了空指针,应该提示...}catch (IndexOutOfBoundsException e2){//2. 如果出现了索引越界, 应该提示...}执行顺序:如果try中代码发生异常, 那么多个catch会从上到下逐一尝试捕获, 如果被A捕获了,后续的catch不再执行。
注意: 1. 前面catch中的异常类型不能是后面catch中的异常类型的父类或者相同 (因为这样的话,后续catch执行不到没有意义)  2. 后面catch中的异常类型可以是前面的父类。
*/
public class TryCatchCatchDemo {public static void main(String[] args) {try{method01(2);}catch (NullPointerException e){System.out.println("发生了空指针异常");}catch (IndexOutOfBoundsException e2){System.out.println("发生了索引越界异常");}System.out.println("代码继续执行");}//模拟: 这段代码可能有两个异常(一段代码里面不论可能有多少个异常,一次运行最多抛出一个异常)private static void method01(int a) {if(a == 1){throw new NullPointerException("空指针"); //throw天生与其他代码互斥,一旦发生throw,其他代码不再运行}else if(a == 2){throw new IndexOutOfBoundsException("越界异常");}else{System.out.println("什么事都没发生");}}
}

在这里插入图片描述

4.finally关键字:catch相当于else if,finally相当于else,return

package com.itheima04.trycatch;
/*try{}catch (Exception e){}finally {// 无论如何,一定最后执行。 作用: (IO流)释放资源}
*/
public class FinallyDemo {public static void main(String[] args) {
//        method();int number =  method2();  //运行 :3System.out.println(number);  //1}	//111111111111111111111111111111111111111111111111111111111111111111111 private static int method2() {int i = 1;try{
//          System.exit(0); //拔电源阻止finallyreturn i;  //一般return后续代码不会执行了,但finally会抢夺try...catch中的return执行权,finally会先执行,执行完又回到return //return 安全机制: 把i的值给记录下来了1 ,所以return 1 }catch (Exception e){ //没有异常,i永远不会=2i = 2;}finally {i = 3;System.out.println("运行 :" + i);}return i; //不执行,因为try catch finally有return了}//11111111111111111111111111111111111111111111111111111111111111111111private static void method() {try{int i = 4/0;System.out.println(i);}catch (NullPointerException e){System.out.println("异常发生了");}finally {System.out.println("无论如何一定执行");}System.out.println("程序继续执行");}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.自定义异常:extends

package com.itheima05.custom;
/*
*   JavaBean : 标准类 (封装)
*       1. private属性
*       2. public get set方法
*       3. public 空参构造,不声明构造会有一个空参构造。 写满参构造就得写空参构造。
*   封装:  1. private属性 为了不对外直接暴露 -> 安全
*          2. public get set方法【方法跟属性最大不同, 方法可以加条件判断(健壮性校验)】
*   继承: 自定义异常
*/
public class Student {private String name;private int age;@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {if(age < 0){
//            throw new IllegalArgumentException("你个傻鸟,age没有负数");throw new NoAgeException("你个傻鸟,age没有负数"); //不报错,因为下面有定义class NoAgeException extends 。。。}this.age = age;}
}
package com.itheima05.custom;
/*
* 自定义异常: IllegalArgumentException : 非法参数异常
* NoAgeException : 非法年龄异常(框架: 很多JDK没有的异常,自定义)
*/
public class CustomDemo {public static void main(String[] args) {Student s = new Student();s.setName("张三");
//        s.age = -18; // 非法访问,安全隐患,所以private int age;s.setAge(-18); //在方法里加安全机制System.out.println(s);}
}
class NoAgeException extends IllegalArgumentException{public NoAgeException(String msg){super(msg); //调用IllegalArgumentException,Ill..又调用自己的super。。}
}
package com.itheima06.notice;
import java.io.IOException;
/*
*   现象: 1.父类 方法中声明编译异常【void method() throws IOException{}】,而子类没有声明编译异常 不会报错
*         2.父类 方法中未声明编译异常,而子类有声明编译异常 会报错 !!!!!
* 
*   解释: 现象2 跟 多态冲突了
*       1.现象2在多态中,编译器只看左边,父类方法没有编译异常,以为没有编译异常,不需要处理
*       2.运行看右边,子类重写的方法运行时有编译异常 (编译异常要求编译时期必须处理)
*       冲突: 编译异常 绕过了 编译器 (编译器强制报错)
*/
public class NoticeDemo {public static void main(String[] args) throws IOException {       Fu fu = new Zi();  //编译看左边,运行看右边fu.method();}
}
class Fu{void method() {}
}
class Zi extends Fu{@Overridevoid method() throws RuntimeException{ //运行异常 不报错 }
}

如下是方法重写注意事项,编译异常报错。
在这里插入图片描述

6.练习:常见异常

package com.atguigu.test01.review;
// 编写代码演示栈内存溢出 StackOverflowError(递归导致内存溢出)
public class TestError1 {public static void main(String[] args) {Son s = new Son();s.test(); //自己调用自己,不调用父类test()方法。java.lang.StackOverflowError}
}
class Father{public void test(){System.out.println("父类的");}
}
class Son extends Father{public void test(){ //调用父类的test();要用super.test()test();System.out.println("子类的");}
}
package com.atguigu.test01.review;
import java.util.ArrayList;
// 请编写代码演示OOM:OutOfMemoryError
public class TestError2 {public static void main(String[] args) {//1、答案一:创建一个超级大数组,//数组的长度的类型是int,Integer是int类型的一个包装类,Integer.MAX_VALUE是2的31次方-1
//		int[] arr = new int[Integer.MAX_VALUE];		//2、答案二:不断的创建对象ArrayList list = new ArrayList();//容器,用来装对象while(true){list.add(new Object());}}
}
public class TestFinallyNoReturn2 {public static void main(String[] args) {int num = getNum(4);System.out.println(num);//0,不是30}public static int getNum(int a){int result = 10;try{System.out.println(a/0); //直接跳到catchif(a > 0){result = 20;return result;}else if(a < 0){result = -20;return result;}else{return result;}}catch(Exception e){System.out.println("exception");result = 0;return result;}finally{result = 30;System.out.println("finally");
//			return result;//如果有这句,最后一行结果就变成30}}
}

在这里插入图片描述
在这里插入图片描述

7.线程两种创建方式:new Thread(new Runnable() {}),extends Thread,implements Runable

在这里插入图片描述
如下FileOutputStream源码中抛出异常,为了让写代码人自己写try catch异常提示信息。
在这里插入图片描述

package com.itheim07.thread;
/*
*   进程和线程
*       1. 进程 :  航空母舰(资源: 燃油 弹药)
*       2. 线程 :  舰载机
*     一个软件运行: 一个军事活动, 必须有一艘航母出去,但执行具体任务的是航母上的舰载机
*     一个软件运行,至少一个进程, 一个进程中至少一个线程。谷歌浏览器是多进程,进程多了,占用资源多,速度快
*	
*   cpu: 4核 8线程。线程要运行,需要cpu授予执行权(指挥室),指挥室可以同时调度8架 飞机
*       1. 并行 : 同一时间,同时执行 (并行只能8线程)
*       2. 并发 : 同一段时间, 实际上是交替执行, 速度快的时候看起来像是同时执行(频率快)(常见: 并发1800线程)
*
*   cpu调度算法(并发)
*       1. 分时调度 : 1800s, 每个线程1s
*       2. 抢占式调度 : 按照线程优先级进行分配, 优先级高(可以自己设置)一般就分配的多(随机性强) java
*
*   为什么需要多线程?
*       1. 默认java代码有两个线程
*           1. main方法线程 : 主线程
*           2. GC线程(jvm使用的,我们无法调度)
*       2. 一个线程可用, 有什么局限性?只能做一件事
*       3. 如果想要同时执行多个任务 -> 多线程
*/
public class ThreadDemo {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("播放音乐...");}}}).start();  //.start()不能改成.run()boolean result = true;while(result){ System.out.println("下载电影...");}/* while(result){ //虽然骗了编译器,但还是不能执行到这里,上面一直在死循环里出不来。System.out.println("播放音乐...");}*/}
}

如下线程第一种创建方式。
在这里插入图片描述

package com.itheima01.thread;
/*Thread:1. start() : 启动线程,jvm会创建线程,并调用run方法2. static Thread currentThread(),返回对当前正在执行的线程对象的引用。3. String getName() : 获取线程名称!!! Thread.currentThread().getName() : 获取当前线程名称线程默认命名规则:1. main线程 :  main2. 子线程(main线程创建的线程) : static int number;static被共享Thread-0 , 1, 2 ...
*/
public class ThreadDemo02 {public static void main(String[] args) {
//        Thread thread = Thread.currentThread();
//        String name = thread.getName(); 
//        System.out.println(name); // main//下面一行等同于上面System.out.println("主:" + Thread.currentThread().getName());YourThread yt = new YourThread();yt.start(); //子:Thread-0        YourThread yt2 = new YourThread();yt.run(); //子:main。  因为子线程YourThread还未执行起飞	,被main飞机拖着走	YourThread yt3 = new YourThread();yt3.start(); //子:Thread-2。  不是Thread-1是因为yt2未起飞但依旧new了yt2//        Person p = new Person(); //执行空参构造
//        System.out.println(p.number); //0 
//        Person p2 = new Person();
//        System.out.println(p2.number); //1}
}
class YourThread extends Thread{@Overridepublic void run() {System.out.println("子:" + Thread.currentThread().getName());}
}
class Person{static int number=-1;public Person(){number++;}
}
package com.itheima02.runnable;
/*
* 线程第二种创建方式: 1. 声明实现 Runnable 接口的类。
*      			       2. 该类然后实现 run 方法。
*       			   3. 然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
*      					Thread(Runnable target)
*/
public class RunnableDemo {public static void main(String[] args) {MyRunnable mr = new MyRunnable(); // 分配该类的实例Thread t = new Thread(mr);t.start();  //Thread-0}
}
class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
package com.itheima02.runnable;
//用匿名内部类简化上面代码
public class RunnableDemo02 {public static void main(String[] args) {		/* Runnable mr = new Runnable(){ //用接口名Runnable代替子类类名,匿名对象。     //不用再写class MyRunnable implements Runnable{},Runnable mr = new MyRunable(); 向上转型     @Override    public void run() {  //new一个接口()再{},是new这个接口的子类对象System.out.println(Thread.currentThread().getName());}};Thread t = new Thread(mr);t.start();// new Thread(mr).start(); *///111111111111111111111111111111111111111111111111111111111111111111111111111111new Thread(new Runnable() {@Overridepublic void run() { //主要关注runSystem.out.println(Thread.currentThread().getName());}}).start();//new Thread(() -> System.out.println(Thread.currentThread().getName())).start();}
}

8.卖票:原子性

package com.itheima03.ticket;
/*
*  需求假设某航空公司有三个窗口发售某日某次航班的100张票,100张票可以作为共享资源,三个售票窗口需要创建三个线程
*  	    好处: 多线程执行同一任务,比较快。
*  	        1. 程序(单线程) , 并发1600线程, cpu分配执行权: 1/1600
*  	        2. 程序(多线程 100)  , 并发1700, cpu分配给我们的程序执行权更多:1/17
*  	    注意: 线程不是越多越好(线程本身很占内存, 慢。票数不多不需要用多线程)。
*/
public class TicketDemo01 {public static void main(String[] args) {MyWindow mw1 = new MyWindow(); //堆中开一块空间 mw1.setName("窗口壹");MyWindow mw2 = new MyWindow(); //同上mw2.setName("窗口222");MyWindow mw3 = new MyWindow(); //同上mw3.setName("窗口三三三");        mw1.start();mw2.start();mw3.start();}
}//11111111111111111111111111111111111111111111111111111111111111111111111111111
class MyWindow extends Thread{static int number = 100; //去掉static,每创建一个MyWindow窗口在堆里开辟一块空间,三个窗口各卖100张@Overridepublic void run() {while(number > 0){System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");number--;}}
}
/*
*  两种线程创建方式: 1.继承Thread  2.实现Runnbale 。第二种方案会更好一些,不需要加static,因为只new了一个对象。
*       1. 实现接口,而不是继承类(扩展性更强) 接口可以多实现,但是类只能单继承(MyWindow继承Thread后,就不能继承另外的类。MyTask可以继承其他类,实现其他接口)
*       2. 更符合 面向对象 (高内聚,低耦合:线程独立,和业务代码MyTask分离,传入卖猪肉任务也行)。封装(各干各的,有必要再进行合作)
*/

如下线程同步问题分析:两种创建方式3个窗口都总卖出102张票,而不是100张。原因:三个窗口同时卡在打印正在卖出第100张票。解决:t1在卖第100张票时,cpu可能会切到t3和t2,可以控制t2和t3不动,等t1的number- -完再动。
在这里插入图片描述

9.线程同步:synchronized关键字/方法,Lock接口,ThreadLocal

package com.itheima04.synchronizedd;
import java.io.IOException;
/*
*       1. 代码块
*           synchronized(锁对象){
*               代码A
*           }
*           1. 锁对象可以是任意对象,但必须唯一
*           2. 同步代码块中的 代码A 同一时间,只允许一个线程执行
* 
*  使用同步锁的注意点:1. 在保证业务逻辑可用的情况,同步锁加的范围越小越好
*  2. 锁对象必须唯一:
*  

如果能保证当前对象唯一,this也可以作为锁对象 (更节省内存) *

当前类名.class(最好的锁对象) -> Class对象(一个类被加载,在内存都会有一个Class对象) 反射 */
public class TicketDemo02 {public static void main(String[] args) {MyTask mt = new MyTask(); //只new了一个,可以用this Thread t1 = new Thread(mt);t1.setName("窗口壹");Thread t2 = new Thread(mt);t2.setName("窗口222");Thread t3 = new Thread(mt);t3.setName("窗口三三三"); t1.start();t2.start();t3.start();} }class MyTask implements Runnable{ int number = 100; // Object obj = new Object(); //锁对象@Overridepublic void run() {while(number > 0){//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 synchronized(MyTask.class){ //MyTask.class也可以换成thisif(number <= 0){break; //跳出while大循环}System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");number--;} //111111111111111111111111111111111111111111111111111111111111111111111111111111111111 //这边只能try catch不能throws,原因:父类Runnable中run方法没有声明抛出编译异常,所以子类也不能throws try {Thread.sleep(1); //线程啥事也不干,暂停1ms,cpu有空闲切换其他线程} catch (InterruptedException e) { //这异常一般发生在线程中,当一个正在执行的线程被中断时就会出现这个异常e.printStackTrace();}} //while里} }

在这里插入图片描述
如下t2卖到0张时出while,而t1和t3还在while里,此时number=0,所以变为0和-1。
在这里插入图片描述
如下把synchronized拖到外面也不行。
在这里插入图片描述
如下加if(number <= 0),没有加浪费时间代码,所以看不到交替效果,但不会出现0和-1。
在这里插入图片描述
obj是锁对象即钥匙,如下钥匙不能进run方法(每个线程一把即三把钥匙了),只能在成员位置。
在这里插入图片描述
用this,不用new object(),可以节约内存。
在这里插入图片描述

package com.itheima05.method;
/*
*   synchronized 方法(同步方法)
*         1. 语法 :  方法声明 + synchronized
*         2. 同步方法有没有锁对象? 有
*               1. 普通方法: 是this
*               2. 静态方法: 静态不能和对象(this)有关。 是当前类名.class
*/
public class TicketDemo02 {public static void main(String[] args) {MyTask mt = new MyTask();        Thread t1 = new Thread(mt);t1.setName("窗口壹");Thread t2 = new Thread(mt);t2.setName("窗口222");Thread t3 = new Thread(mt);t3.setName("窗口三三三");        t1.start();t2.start();t3.start();}
}class MyTask implements Runnable{static int number = 100;@Overridepublic void run() { while(number > 0){method(); //非静态方法可以调用静态方法       try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}private static synchronized void method() { //静态方法不能和对象关键字如this相关  //同步方法效果 等价于 同步代码块if(number <= 0){return;  //break只能写在循环和switch里}System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");number--;}
}
package com.itheima06.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
*   Lock接口: 1. 实现类 ReentrantLock
*             2. lock() 获取锁(获取钥匙)
*             3. unlock() 释放锁 (还钥匙)
*/
public class TicketDemo02 {public static void main(String[] args) {MyTask mt = new MyTask();Thread t1 = new Thread(mt);t1.setName("窗口壹");Thread t2 = new Thread(mt);t2.setName("窗口222");Thread t3 = new Thread(mt);   t3.setName("窗口三三三"); t1.start();t2.start();t3.start();}
}class MyTask implements Runnable{int number = 100;Lock lock = new ReentrantLock(); //创建lock对象@Overridepublic void run() {while(number > 0){//1111111111111111111111111111111111111111111111111111111111111111111111111lock.lock();if(number <= 0){
//                System.out.println(Thread.currentThread().getName());lock.unlock(); // 注意: lock提供了锁的可视化操作(线程执行结束,要记得手动释放。厕所上完不能带走钥匙)//同步代码块return或break后是jvm自动释放锁。//这里不加lock.unlock()程序停不下来。break;}System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");number--;lock.unlock();}}
}

如下t1和t2两个线程隔离。
在这里插入图片描述
t1(…,User),每次传User对象显得麻烦。如下可将User放入ThreadLocal中,每次在每个函数中通过ThreadLocal.get拿到线程的User。
在这里插入图片描述
如下一个请求过来的生命周期是先要经过过滤器/拦截器再到servlet,才会有后续的视图等,这是springmvc的一个生命周期。
在这里插入图片描述
在这里插入图片描述
如下可看出在MyFilter中设置了ThreadLocal相当于设置了整条请求(经过filter - servlet - 拦截器等等),一条路上整个都会有上下文,在当前线程的上下文拿到这个值。
在这里插入图片描述
每个线程都是有ThreadLocal上下文的,线程执行完后并没有销毁,原因是springmvc用的是线程池,这个线程池线程对象并没有销毁,下一次给其他请求再去使用,再去使用时这个线程里的ThreadLocal其实是上一次即线程前世所设置的值,所以当前ThreadLocal用完后要清理掉。
在这里插入图片描述
ThreadLocal(线程本地)设为的是一个全局变量,为什么在不同线程中get和set能隔离?ThreadLocal本身不存储任何值,如下的value就是t.get和set的值
在这里插入图片描述

10.卖包子:wait,notify

package com.itheima07.bz;public class Demo {public static void main(String[] args) throws InterruptedException {Object obj = new Object();       
// obj.wait(); //IllegalMonitorStateException : 非法的监视状态异常,因为.wait()必须锁对象调用如下synchronized (obj){  //对象变成锁对象obj.wait(); //不会报错,一直等待。在锁对象中}}
}

如下两个方法wait和notify不是给线程调用的,而是给锁对象【锁对象可以是任意对象】调用的如上所示。BaoZi只能一个线程对其操作。
在这里插入图片描述

package com.itheima07.bz;public class BaoZi {boolean isHave=false; //默认没有包子
}
package com.itheima07.bz;public class BaoziPu extends Thread {BaoZi bz;public BaoziPu(BaoZi bz){this.bz = bz;}@Overridepublic void run() {       while(true){ //不停生产包子 //111111111111111111111111111111111111111111111111111111111111111111111111111111           synchronized (bz){ //加锁: 同步代码,生产包子时不让别人打扰我。注意下面wait和notifyif(bz.isHave){try {bz.wait(); //包子铺有包子就等待(此时吃货正在吃包子)} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("包子铺生产包子..."); //没包子bz.isHave = true;bz.notify(); //唤醒吃货}}  //while里}
}
package com.itheima07.bz;public class ChiHuo  extends Thread{BaoZi bz;public ChiHuo(BaoZi bz){this.bz = bz;}@Overridepublic void run() {while(true){ //不停吃包子//1111111111111111111111111111111111111111111111111111111111111111111111111111               synchronized (bz){if(!bz.isHave){try {bz.wait(); //吃货没有包子就等待(此时包子铺正在生产包子)} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("吃货吃包子"); //有包子bz.isHave = false;       bz.notify(); //唤醒包子铺}}}
}
package com.itheima07.bz;public class BzDemo {public static void main(String[] args) {BaoZi bz = new BaoZi();BaoziPu bzp = new BaoziPu(bz); //和下面一行共同操作一个包子对象ChiHuo ch = new ChiHuo(bz);        bzp.start();ch.start();}
}

在这里插入图片描述
如下第一次没有包子,所以绕过2中if到1。运行完1后就有包子了,1时间很短,cpu不切换线程,切换了也没用,因为2中syn…(bz)包子被锁住,就算切换到吃货线程进不去syn…(bz)里,所以1中notify唤不醒吃货线程。

1和2都在sy…(bz)里,bzp线程bz.wait()【有3个好处】进入等待状态即进入监视队列即等待包子被吃,吃货线程的synchronized锁被打开,有包子不会wait,执行3。

一个线程wait把自己停下来放入堆(监视队列)中,来年开春,另一个线程中3叫我起来干活。2和3对应,1和4对应。3唤醒了2中wait,但2没钥匙(锁)动不了(鬼压床),钥匙在吃货手上,所以3往后4执行释放锁,1234不停循环执行
在这里插入图片描述
生产消费者模型:用户发请求来相当于包子铺生产包子即生产者服务器24小时开着相当于消费者一天24小时等包子吃。不会让消费者线程空转浪费cpu资源,所以没包子设置消费者线程为wait状态不占用cpu资源

package com.atguigu.test14;
// 线程通信是用来解决生产者与消费者问题。
public class Test14 {public static void main(String[] args) {Workbench tai = new Workbench(); //相当于包子	Cook c = new Cook("崔志恒", tai); //生产者Waiter w = new Waiter("翠花", tai);	//消费者c.start();w.start();}
}//11111111111111111111111111111111111111111111111111111111111111111111111111
class Workbench{private static final int MAX = 10; //假设工作台上最多能够放10盘private int count; //count是共用的,要考虑线程安全	public synchronized void put(){ //同步方法,非静态方法来说,锁对象就是this //往工作台上放一盘菜if(count >= MAX){try {//生产者停下来,等待wait();//默认是this.wait(),所以上面必须加锁对象synchronized} catch (InterruptedException e) {e.printStackTrace();}}//上面是安全校验count++;		System.out.println(Thread.currentThread().getName() + "放了一盘菜,剩余:" + count);this.notify(); // 包子/工作台.notify()  //唤醒消费者}	//1111111111111111111111111111111111111111111111111111111111111111111111111111public synchronized void take(){//从工作台上取走一盘菜if(count<=0){try {				wait(); //工作台没有菜,消费者应该停下来} catch (InterruptedException e) {e.printStackTrace();}}//上面是安全校验count--;System.out.println(Thread.currentThread().getName() + "取走一盘菜,剩余:" + count);this.notify();  //唤醒生产者}
}//1111111111111111111111111111111111111111111111111111111111111111111111111
class Cook extends Thread{private Workbench tai;public Cook(String name, Workbench tai) {super(name);this.tai = tai;}public void run(){while(true){tai.put(); //封装了try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}//111111111111111111111111111111111111111111111111111111111111111111111
class Waiter extends Thread{private Workbench tai;	public Waiter(String name, Workbench tai) {super(name); //name属性在父类中已声明this.tai = tai;}public void run(){while(true){tai.take();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

如下一直交替运行,不停。
在这里插入图片描述
如下线程6态:锁就是钥匙上厕所,限时等待就是sleep,记住blocked,waitting,runnable。
在这里插入图片描述
如下B进不去不执行。
在这里插入图片描述

11.在子线程中输出1-100之间的偶数,主线程输出1-100之间的奇数

package com.atguigu.test02.homework01;public class Test01 {public static void main(String[] args) {new Thread(){public void run(){for (int i = 0; i <= 100; i+=2) {System.out.println("子线程:" + i);}}}.start();		for (int i = 1; i <= 100; i+=2) {System.out.println("主线程:" + i);}}
}

12.创建和启动2个子线程:一个打印1-10之间奇数,一个打印1-10之间偶数

package com.atguigu.test03.homework02;
//(1)要求每个线程要么不打印,要么就连续打印5个数,每个数打印间隔500毫秒 。(2)但两个线程不要求交替打印。
public class Test02 {public static void main(String[] args) {Odd o = new Odd();Even e = new Even();		o.start();e.start();}
}class Odd extends Thread{private int num = 1;public void run(){while(true){synchronized (Thread.class) {for (int i = 1; i <=5; i++) {System.out.println("奇数线程,第"  + i + "个:" + num);num += 2;try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}			}}
}class Even extends Thread{private int num = 0;public void run(){while(true){synchronized (Thread.class) {for (int i = 1; i<=5; i++) {System.out.println("偶数线程,第"  + i + "个:" + num);num += 2;try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}}}
}

在这里插入图片描述

13.创建和启动2个子线程:一个打印奇数,一个打印偶数

package com.atguigu.test05.homework03.copy;
//(1)要求实现交替打印。(2)每个数打印间隔1秒
public class Test03 {public static void main(String[] args) {MyThread t1 = new MyThread();MyThread t2 = new MyThread();		t1.start();t2.start();}
}class PrintNumber{private static int num = 1;	public static synchronized void print(){try {PrintNumber.class.notify();System.out.println(Thread.currentThread().getName() + ":" + num);num++;PrintNumber.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}
}class MyThread extends Thread{public void run(){while(true){PrintNumber.print(); //静态方法try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

在这里插入图片描述

14.使用两个线程循环打印出1~100

package com.itheima.demo01;class Num {int i;boolean falg;
}class TestThread1 {public static void main(String[] args) {Num num=new Num();Thread t1=new Thread(new A(num));Thread t2=new Thread(new B(num));t1.start();t2.start();}
}//1111111111111111111111111111111111111111111111111111111111111111111111
class A implements Runnable{private Num num;public A(Num num) {this.num=num;}@Overridepublic void run() {while(num.i<99){synchronized(num){//1111111111111111111111111111111111111111111111111111111111111111111if(num.falg){try {num.wait();} catch (InterruptedException e) {}}num.i++;num.falg=true;System.out.println(Thread.currentThread().getName()+"-"+num.i);num.notify();}}}
}//1111111111111111111111111111111111111111111111111111111111111111111111
class B implements Runnable{private Num num;B(Num num){this.num=num;}@Overridepublic void run() {while(num.i<99){synchronized(num){//11111111111111111111111111111111111111111111111111111111111111111if(!num.falg){try {num.wait();} catch (InterruptedException e) {}}num.i++;num.falg=false;System.out.println(Thread.currentThread().getName()+"-"+num.i);num.notify();}}}
}

在这里插入图片描述

15.使用三个线程循环打印出1~100

package com.itheima.demo01;class TestThread2 {public static void main(String[] args) throws Exception{Thread t1 = new Thread(new MyThread1(0));Thread t2 = new Thread(new MyThread1(1));Thread t3 = new Thread(new MyThread1(2));t1.start();t2.start();t3.start();}static class MyThread1 implements Runnable {private static Object lock = new Object();private static int count = 0;int no;public MyThread1(int no) {this.no = no;}@Overridepublic void run() {while (true) {synchronized (lock) {//11111111111111111111111111111111111111111111111111111111111111111111if (count >= 100) {break;}if (count % 3 == this.no) {count++;System.out.println(this.no + "--->" + count);} else {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}lock.notifyAll();}}}}
}

在这里插入图片描述

16.账户类:synchronized 方法

package com.atguigu.test06.homework04;
/*案例:1、创建一个银行账户类:(1)属性:账号,余额。(2)get/set。(3)toString():返回:账户:xxx,余额:xxx2、创建一个丈夫类:负责往里存钱,每次存款[0,10000)以内不等3、创建一个妻子类:负责取钱,每次取款[0,10000)以内不等,如果余额不足,要等丈夫存够了才能取
*/
public class Test04 {public static void main(String[] args) {Account a = new Account("1122", 0);AccountManager am = new AccountManager(a);Husband h = new Husband("崔志恒",am);Wife w = new Wife("甄玉禄",am);		h.start();w.start();}
}//1.11111111111111111111111111111111111111111111111111111111111111111111111
class Husband extends Thread{private AccountManager am;public Husband(String name,AccountManager am) {super(name);this.am = am;}public void run(){while(true){am.save();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}
}//2.11111111111111111111111111111111111111111111111111111111111111111111111
class Wife extends Thread{private AccountManager am;public Wife(String name,AccountManager am) {super(name);this.am = am;}public void run(){while(true){am.withdraw();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}
}//3.11111111111111111111111111111111111111111111111111111111111111111111111
class AccountManager{private Account account;	public AccountManager(Account account) {super();this.account = account;}		public synchronized void save(){double money = Math.random() * 10000;System.out.println(Thread.currentThread().getName() + "开始存钱,目前账户状态:" + account); //自动调用account.tostring方法System.out.println("本次存钱的数量是:" + money);account.setBalance(account.getBalance() + money);System.out.println(Thread.currentThread().getName() +  "存钱结束,目前账户状态: " + account);this.notify();}	public synchronized void withdraw(){double money = Math.random() * 10000;System.out.println(Thread.currentThread().getName() + "开始取钱,目前账户状态:" + account);while(money > account.getBalance()){try {System.out.println("本次想取钱的数量是:" + money + ",余额不足....");this.wait();} catch (InterruptedException e) {e.printStackTrace();}}account.setBalance(account.getBalance() - money);System.out.println(Thread.currentThread().getName() +  "取钱结束,目前账户状态: " + account);}
}//4.111111111111111111111111111111111111111111111111111111111111111111111111
class Account{private String id;private double balance;public Account(String id, double balance) {super();this.id = id;this.balance = balance;}public Account() {super();}public String getId() {return id;}public void setId(String id) {this.id = id;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}@Overridepublic String toString() {		return "账户: " + id +"余额:" + balance;  //账户:xxx,余额:xxx}	
}

在这里插入图片描述

17.模拟多个人通过一个山洞:synchronized 方法

package com.atguigu.test07.homework05;
/*
​	1、这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒;
​	2、随机生成10个人,同时准备过此山洞
​	3、定义一个变量用于记录通过隧道的人数
​	4、显示每次通过山洞人的姓名,和通过顺序;*/
public class Test05 {public static void main(String[] args) {Tunnel t = new Tunnel();		for (int i = 1; i <= 10; i++) {Thread th = new Thread(t, "p" + i);th.start();}}
}//111111111111111111111111111111111111111111111111111111111111111111111111111
class Tunnel implements Runnable{private int num;	public void run(){cross();}		public synchronized void cross(){ //每次只能由一个线程执行,相当于是一个人通过System.out.println(Thread.currentThread().getName() + "准备开始通过...");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}num++;System.out.println(Thread.currentThread().getName()+"通过了隧道,TA是第" + num);}
}

在这里插入图片描述
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部