Java学习_Day17

多线程
1.相关概念
并发:指两个或多个事件在同一段时间里发生
并行:指两个或多个事件在同一时刻发生(同一时间)
程序:有某种编程语言编写的一套有序的指令
软件:由若干个程序+其他资源组成的软件(qq由后台程序+前台静态资源)
进程:软件的运行时系统为其开辟的一块独立的空间
线程:是进程的最小单位,一个进程必定最少有一个线程,如果有多线程则被称为多线程程序

		2.线程的调度机制
			分时调度--->平均
			抢占式调度----->优先级高的抢的多,如果优先级一样,cpu随机选择,java采用这个

		3.线程的创建和启动
			A.继承Thread类
				①新建一个类
				②继承Thread类
				③重写父类run方法(当前运行的主题)
				④实例化一个当前类的对象(线程对象)
				⑤启动线程(线程对象.start())
			B.实现Runnable接口
				①新建一个类
				②实现接口Runnable接口
				③实现父接口run方法(当前运行的主题)
				④实例化一个当前类的对象(目标对象)
				⑤实例化一个Thread类的对象,将目标设置进去(线程对象)
					//Thread red =new Thread(目标对象的对象名);
				⑥启动线程
			4.Thread线程核心类
				必须调用的Thread类的构造方法
				构造方法:
					new THread() 供子类使用
					new Thread(String name)  供子类使用  (---》String name 线程的名)
					new Thread(Runnable target)  自己实例化,并设置目标
					new Thread(Runnable target,String name)   自己实例化,并设置目标 (---》String name 线程的名)

			方法:
					public String getName()   获取当前线程的名称
					public void run()  此线程要执行的任务在此处定义代码
					public static Thread currentThread()  返回当前线程正对执行线程对象的引用
							[Thread-0,5,main]   【线程名,线程优先级,调用的方法】
					public final boolean isAlive():测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。						
					优先级:【1-10】
					 public final int getPriority() :返回线程优先级
                    public final void setPriority(int newPriority) :改变线程的优先级
                    线程的默认优先级和创建他的那个线程优先级一致
                    main方法的线程优先级默认是5!
					                 
		        public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
                public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
                public static void yield():线程的礼让
                void join() :线程的插队
                void join(long millis) :
                void join(long millis, int nanos) :
					
	    练习:
        创建一个子线程,打印0-100之间的偶数,创建一个子线程,打印0-100之间的奇数
        main方法就作为启动这两个子线程的线程
/**
 *         练习:
 *             创建一个子线程,打印0-100之间的偶数,创建一个子线程,打印0-100之间的奇数
 *             main方法就作为启动这两个子线程的线程
 */
public class Test1 {
    public static void main(String[] args) {
        Treand1 t1 = new Treand1();
        Treand2 t2 = new Treand2();


        t1.start();
        t2.start();


    }
}



public class Treand1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <=100; i++) {
            if (i%2==0)
                System.out.println(this.getName()+"这是偶数="+i);
            if (i==50) {
                try {
                    this.join(1000);//如果不设置时间  此线程会一致在50这里卡住
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



public class Treand2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <=100; i++) {
            if (i%2==1)
                System.out.println(this.getName()+"这是奇数="+i);
            if (i==9){
                this.yield();
            }
        }
    }
}

线程安全
1.同步代码块
语法:synchronized(锁对象){需要上锁的代码}
需要上锁的代码:在保证程序正确的前提下,代码越少越好
锁对象:任意Java对象,但是要报错多个线程想要互斥,那么这多个线程的锁对象必须是同一个那么者多个线程
1.2 同步方法
语法:在方法上添加修饰符synchronized
普通方法的锁对象默认是 this
静态方法的锁对象默认是 this.getClass()
任意的String对象 如“java” “a” “ads"
练习:买票问题?
接口:


public class Tickets2 implements Runnable {
     private int num= 100;
    @Override
    public void run() {
        {
            //为了防止重复,限制每次只能进入一个线程 增加同步锁(互斥锁)
            while (true){
                System.out.println("请问您买票吗");
                //一般用: this   this.getClass()  常量字符串
                synchronized (this) { //String对象  /  this.getClass
                    if (num>=0){
                        System.out.println(Thread.currentThread().getName()+"买到一张票,剩余票数="+num);
                        --num;
                    }else {
                        break;
                    }
                }
            }
        }
    }
}


public class Test2 {
    public static void main(String[] args) {
        Tickets2 tickets = new Tickets2();

        Thread t1 = new Thread(tickets);
        Thread t2 = new Thread(tickets);
        Thread t3 = new Thread(tickets);

        t1.start(); //这是调用线程
        t2.start();
        t3.start();
    }
}

继承:


public class Tickets extends  Thread{
    private static int num =100; //总票数
    @Override
    public void run() {
        //为了防止重复,限制每次只能进入一个线程 增加同步锁(互斥锁)
            while (true){
                System.out.println("请问您买票吗");
                //一般用: this   this.getClass()  常量字符串
             synchronized ("s") { //因为同步代码块的要求 所以这里只能采用this.getClass()  常量字符串
                if (num>=0){
                    System.out.println(Thread.currentThread().getName()+"买到一张票,剩余票数="+num);
                    --num;
                }else {
                    break;
                }
            }
        }
    }
}

/**线程
 * 继承
 * 同步代码块
 * 要求:多个线程的锁对象必须是同一个
 */
public class Test1 {
    /**
     * 单元测试不能测试线程
     * @param args
     */
    public static void main(String[] args) {
        Tickets tickets1 = new Tickets();
        Tickets tickets2 = new Tickets();
        Tickets tickets3 = new Tickets();

        tickets1.start(); //这是调用线程
        tickets2.start();
        tickets3.start();

//        tickets1.run();  //这是调用方法
//        tickets2.run();
//        tickets3.run();


    }
}


		**线程的通信**
				等待唤醒---.> 生产者和消费者模式
				等待:
						wait()
						wait(long timeout)
						wait(long timeout,int  nanos)
								运行等待方法的线程进行等待
								 调用等待方法的对象是监视器对象(就是后期唤醒该线程的依据)
			                要求:等待方法必须运行在**锁内**,并且锁对象必须和监视器对象**同一个**
			                原理:当前线程执行等待方法后,会释放锁资源
			                     在被唤醒之后,依然会判断锁资源是否存在,如果不存在就等着
			                        如果存在的话,是接着上次等待的位置往下执行!
			            唤醒:
			                notify()
			                    唤醒的是对象监视器下等待的一个线程
			                notifyAll()
			                    唤醒的是对象监视器下等待的所有线程
			                要求:唤醒方法必须运行在**锁内**,并且**锁对象必须和监视器对象同一个**

练习: 一对一吃包子的问题

public class Test2 {
    public static void main(String[] args) {
        //创建一个包子对象
        BaoZi baoZi=new BaoZi();

        BaoZiPu baoZiPu=new BaoZiPu(baoZi);
        ChiHuo chiHuo=new ChiHuo(baoZi);

        baoZiPu.start();
        chiHuo.start();
    }
}




/**
 * 练习:一对一吃包子  一个生产者和一个消费者
 */

public class ChiHuo extends Thread {
    private BaoZi baoZi;
    public ChiHuo(BaoZi baoZi){
        this.baoZi=baoZi;
    }
    @Override
    public void run() {
        while(true){
            //包子不存在,自己等待
            synchronized ("java") {
                if(!baoZi.isFlag()){
                    try {
                        "java".wait(); //等待的时候会释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("开始吃包子");
                baoZi.setFlag(false);
                System.out.println("包子吃完了,请继续制作");
                "java".notify();
            }
        }
    }
}


public class BaoZiPu extends Thread {
    private BaoZi baoZi;
    public BaoZiPu(BaoZi baoZi){
        this.baoZi=baoZi;
    }
    @Override
    public void run() {

        while(true){
            //包子存在,自己等待
            synchronized ("java") {
                if(baoZi.isFlag()){
                    try {
                        "java".wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("开始制作包子");
                for (int i = 0; i < 3; i++) {
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("第"+(i+1)+"步");
                }
                baoZi.setPer("白面");
                baoZi.setXian("猪肉大葱");
                baoZi.setFlag(true);
                System.out.println("包子制作完成,吃货来吃");
                "java".notify();
            }
        }
    }
}


public class BaoZi {
    private String per;
    private String xian;
    private boolean flag =true;//记录包子资源是否存在  true就是存在  false就是不存在

    public String getPer() {
        return per;
    }

    public void setPer(String per) {
        this.per = per;
    }

    public String getXian() {
        return xian;
    }

    public void setXian(String xian) {
        this.xian = xian;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}


**线程的生命周期**
		①新生线程-------->线程对象刚刚被实例化出来
		②就绪线程---------->调用start方法
		③运行线程---------->线程得到执行权(先进入同步锁,关闭同步锁)
		④阻塞线程
					睡眠   slee()
					插队   join()
					等待   wait()
					锁  synchronized
					IO阻塞
					........
			⑤死亡线程
					run方法结束-->正常结束/非正常结束(system.exit(0))
	**实现接口Runnable的好处**
		 a. 避免了java中的单继承的局限性
        b. 可以更好的处理共享资源
        c. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
        d. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
       **释放锁和不释放锁**
	             释放锁:
	                a. 上锁的代码运行完了(锁方法/锁代码块运行完;break,return终止了锁方法/锁代码块;代码出现异常报错,结束了运行)
	                b. 执行了wait方法
	            不释放锁:
	                a. 上锁的代码没有运行完
	                b.Thread.sleep()、Thread.yield()(暂停当前线程的执行)
		死锁  ->  一定要避免的
        sleep和wait的区别
	            sleep不会释放锁
	            wait会释放锁		

泛型

  概念:在定义一些内容的时候,有一些数据的数据类型不能够确定,就定义为泛型
            然后在使用的时候在确定其类型
  好处:可以避免强转,减少代码量和异常出现的几率
            Comparable
            Comparator
/**
 * 泛型的应用
 */
public class Test1 {
    public static void main(String[] args) {
//范围没有基本类型,基本类型用包装类代替
        Student<Double> s1 = new Student<Double>();
        s1.setScore(55.7);
//        泛型可以只前面定义 后面可以省略
        Student<String> s2 = new Student();
        s2.setScore("优秀");

        System.out.println(s1);
        System.out.println(s2);

    }
}



public class Student<T> {
    private String name;
    /**
     *eg:数学:double 98.5  99.5 875
     *     语文:string   优良差
     *     英语:A B C
     *     同一个变量,在不同的环境下有不同的类型表示,这时候可以定义为泛型
     */
    private T score;

    public Student() {
    }

    public Student(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}


import java.util.Comparator;

public class Student implements Comparator<Student> {
    private String name;
    /**
     *eg:Comparable
     */
    private int score;

    public Student() {
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }



    public void setScore(int score) {
        this.score = score;
    }



    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }




    @Override
    public int compare(Student o1, Student o2) {
        return o1.score-o2.score;
    }
}
	泛型的定义:一般是一个大写字母
			1.类、接口
					语法:类名/接口名<字母1,字母2....>
					要求:泛型可以作为属性,形参,返回值,但是不可以使用在静态资源上面 (泛型是在实例化的时候才知道类型,才去开辟空间,而静态类却是在类加载前都开辟了空间)
					使用:
						实例化对象:
	                        Student<String,Integer> student=new Student<>();
	                        泛型的类型必须是引用数据类型
	                        泛型要么都指定类型,要么都不指定类型(类型默认是Object)
	                   作为父级:
	                        a. 在继承的时候就确定父类的泛型类型
	                            public class Sub extends Student<String,Double> {}
	                            每次实例化子类对象的时候,泛型都是固定的啊
	                        b. 在实例化子类对象的时候确定父类的泛型类型
	                            public class Sub<T,A> extends Student<T,A> {}
	                            Sub<String,Double> sub=new Sub<>();
	                            每次实例化子类对象,都可以设置不同的泛型类型

							
			2.方法

	3. 类型变量的上限(父类或父接口的类型)
        <类型 extends 上限>
            上限可以是类,也可以是接口
            要求:如果上限是类的话,只能有一个
                 如果上限是接口,可以有多个
                 如果类和接口同时存在,可以,但是需要满足上述要求,另外类必须在首位
    4. 泛型的擦除
        如果不指定泛型的实际类型,则采用泛型的上限类型

	 5. 泛型方法
        将泛型定义在方法上,泛型的使用范围只仅限于当前方法
        语法:在方法修饰符的后面添加泛型代码
            public static<T extends Comparable> void sort(T[] arrs){
        注意:当前泛型只能在当前方法内使用
              方法上的泛型在确定类型是通过,实参的类型确定的

import java.util.Arrays;
import java.util.Comparator;

public class Utils<T> {
//静态类和静态方法并不属于静态资源
    public  static<T extends Comparable> void method(T[] arrs){  //这里可以使用static
        //冒泡
        for (int i = 0; i < arrs.length-1; i++) {
            for (int j = 0; j < arrs.length-i-1; j++) {
                if (arrs[j].compareTo(arrs[j+1])<0){
                    T temp =arrs[j];
                    arrs[j]=arrs[j+1];
                    arrs[j+1]=temp;
                }
            }
        }
    }

    public  static<T extends Comparator> void method1(T[] arrs){  //这里可以使用static
                Arrays.sort(arrs);
    }
}


/**
 * 泛型的应用
 */
public class Test1 {
    public static void main(String[] args) {
        Student[] ly = new Student[7];
         ly[0] = new Student("ly", 5);
        ly[1] = new Student("ly", 52);
         ly[2] = new Student("ly", 59);
         ly[3] = new Student("ly", 5);
        ly[4] = new Student("ly", 51);
        ly[5] = new Student("ly", 25);
         ly[6] = new Student("ly", 65);

        Utils.method1(ly);

        for (Student student : ly) {
            System.out.println(student);
        }

        System.out.println();
                Utils<Student> s = new Utils<>();
        s.method(ly);

        for (Student student : ly) {
            System.out.println(student);
        }
    }
}


import java.util.Comparator;

public class Student implements Comparable<Student>,Comparator<Student>{
    private String name;

    private int  score;

    public Student() {
    }

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }


    @Override
    public int compare(Student o1, Student o2) {
        return o1.score-o2.score;
    }

    @Override
    public int compareTo(Student o) {
        return this.score-o.score;
    }
}


    6 泛型的通配符
        一般用在方法的形参
        <?>   任意类型    默认就是任意类型
        <? extends 上限类型>   对局部的要求上限类型(上限类型或者上限类型的子类)
        <? super 下限类型>    对局部的要求下限类型(只能是下线类型或者是下线类型的父类)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值