A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

###24.01_多线程(多线程的引入)(了解)
* 1.什么是线程
        * 线程是程序执行的一条路径,一个进程中可以包含多条线程
        * 多线程并发执行可以提高程序的效率,可以同时完成多项工作
* 2.多线程的应用场景
        * 红蜘蛛同时共享屏幕给多个电脑
        * 迅雷开启多条线程一起下载
        * QQ同时和多个人一起视频
        * 服务器同时处理多个客户端请求
       
###24.02_多线程(多线程并行和并发的区别)(了解)
* 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行(需要多核CPU)
* 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,
  使人感觉两个任务都在运行
* 举例:我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行
* 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊,这就叫并发

###24.03_多线程(Java程序运行原理和JVM的启动是多线程的吗)(了解)
* A:Java程序运行原理
        * Java命令会启动java虚拟机,启动JVM等于启动了一个应用程序,也就是启动了一个进程,
          该进程会自动启动一个"主线程",然后主线程去调用某个类的main方法
       
* B:JVM的启动是多线程的吗
        * JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的
代码演示如下:
public class Demo1_Thread {
        //证明jvm是多线程的
        public static void main(String[] args) {
                for(int i = 0; i < 100000; i++) {
                        new Demo();//产生很多垃圾,
                }
               
                for(int i = 0; i < 10000; i++) {
                        System.out.println("我是主线程的执行代码");//交替输出
                }
        }
}

class Demo {

        @Override
        public void finalize() {
                System.out.println("垃圾被清扫了");//交替输出,说明JVM启动至少启动了垃圾回收线程和主线程
        }       
}

###24.04_多线程(多线程程序实现的方式1)(掌握)
* 1.继承Thread
        * 定义类继承Thread
        * 重写run方法
        * 把新线程要做的事写在run方法中
        * 创建线程对象
        * 开启新线程,内部会自动执行run方法
代码演示如下:       
        public class Demo2_Thread {
                public static void main(String[] args) {
                        MyThread mt = new MyThread();//4,创建自定义类的对象
                        mt.start();//5,开启线程
                       
                        for(int i = 0; i < 3000; i++) {
                        System.out.println("主线程bbb");//开启子线程的时间CPU切换很快,所以主线程先输出内容,
                        }
                }
        }

        class MyThread extends Thread {//1,定义类继承Thread
                public void run() {                                                                                        //2,重写run方法
                        for(int i = 0; i < 3000; i++) {//3,将要执行的代码,写在run方法中
                        System.out.println("子线程aaa");//子线程要开启完毕后,才调用run方法执行输出内容
                        }
                }
        }
总结:在子线程开启还没有完成的一定时间里,主线程就把内容输出一部分,说明CPU切换很快,
然后子线程开启后,两个线程抢占资源分别交替输出

###24.05_多线程(多线程程序实现的方式2)(掌握)
* 2.实现Runnable
        * 定义类实现Runnable接口
        * 实现run方法
        * 把新线程要做的事写在run方法中
        * 创建自定义的Runnable的子类对象
        * 创建Thread对象,传入Runnable
        * 调用start()开启新线程,内部会自动调用Runnable的run()方法
代码演示如下:
        public class Demo3_Runnable {
                public static void main(String[] args) {
                        MyRunnable mr = new MyRunnable();//4,创建自定义类对象
                        Thread t = new Thread(mr);//5,将其当作参数传递给Thread的构造函数
                        t.start();//6,开启线程
                       
                        for(int i = 0; i < 3000; i++) {
                                System.out.println("主线程bb");
                        }
                }
        }
       
        class MyRunnable implements Runnable {//1,自定义类实现Runnable接口

                @Override
                public void run() {//2,重写run方法
                        for(int i = 0; i < 3000; i++) {//3,将要执行的代码,写在run方法中
                                System.out.println("子线程aa");
                        }
                }               
        }

###24.06_多线程(实现Runnable的原理)(了解)
* 查看源码
        * 1,看Thread类的构造函数,传递了Runnable接口的引用
        * 2,通过init()方法找到传递的target给成员变量的target赋值
        * 3,查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

###24.07_多线程(两种方式的区别)(掌握)
* 查看源码的区别:
        * a.继承Thread:由于子类重写了Thread类的run(),当调用start()时,直接找子类的run()方法
        * b.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时,
          内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),
          运行时执行的是子类的run()方法
       
* 继承Thread
        * 好处是:可以直接使用Thread类中的方法,代码简单
        * 弊端是:如果已经有了父类,就不能用这种方法
* 实现Runnable接口
        * 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
        * 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

###24.08_多线程(匿名内部类实现线程的两种方式)(掌握)
* 继承Thread类
代码演示如下:        
                new Thread() {//1,new 类(){}继承这个类
                        public void run() {//2,重写run方法
                                for(int i = 0; i < 3000; i++) {//3,将要执行的代码,写在run方法中
                                        System.out.println("子线程aa");
                                }
                        }
                }.start();

* 实现Runnable接口
代码演示如下:                       
                new Thread(new Runnable(){//1,new 接口(){}实现这个接口
                        public void run() {//2,重写run方法
                                for(int i = 0; i < 3000; i++) {//3,将要执行的代码,写在run方法中
                                        System.out.println("子线程bb");
                                }
                        }
                }).start();

###24.09_多线程(获取名字和设置名字)(掌握)
* 1.获取名字
        * 通过getName()方法获取线程对象的名字//默认从Thread-0开始命名,下一个为Thread-1
* 2.设置名字
        * 通过构造函数可以传入String类型的名字:,或者通过setName(String)方法可以设置线程对象的名字
代码演示如下:
        new Thread("xxx") {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                                System.out.println(this.getName() + "...aa");//xxx...aa
                        }
                }
        }.start();
       
        new Thread("yyy") {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                                System.out.println(this.getName() + "...bb");//yyy...bb
                        }
                }
        }.start();

        * 通过setName(String)方法可以设置线程对象的名字:
代码演示如下:
        Thread t1 = new Thread() {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                                System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
                        }
                }
        };
       
        Thread t2 = new Thread() {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                                System.out.println(this.getName() + "....bb");
                        }
                }
        };
        t1.setName("芙蓉姐姐");
        t2.setName("凤姐");
       
        t1.start();
        t2.start();

总结:通过构造传入名字或者通过对象调用setName方法设置名字,封装的跟我们平时写的Javabean一样,对吧?

###24.10_多线程(获取当前线程的对象)(掌握)
* Thread.currentThread(),主线程也可以获取
代码演示如下:
        new Thread(new Runnable() {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                        System.out.println(Thread.currentThread().getName() + "...a");//Thread-0....aaaaaa
                        }
                }
        }).start();
       
        new Thread(new Runnable() {
                public void run() {
                        for(int i = 0; i < 1000; i++) {
                        System.out.println(Thread.currentThread().getName() + "...bb");//Thread-1...bb
                        }
                }
        }).start();
        Thread.currentThread().setName("我是主线程");//获取主函数线程的引用,并改名字
        System.out.println(Thread.currentThread().getName());//获取主函数线程的引用,并获取名字

总结:线程的默认名字Thread-0也是从构造调用设置的,看构造源码就知道了,如下所示:
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);//nextThreadNum方法返回值threadInitNumber默认从0开始
    }

###24.11_多线程(休眠线程)(掌握)
* Thread.sleep(毫秒,纳秒),控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000
代码演示如下:
        new Thread() {
                public void run() {
                        for(int i = 0; i < 10; i++) {
                                System.out.println(getName() + "...aa");//Thread-0...aa
                                try {
                                        Thread.sleep(1000);//让当前线程睡眠1秒
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        }.start();
       
        new Thread() {
                public void run() {
                        for(int i = 0; i < 10; i++) {
                                System.out.println(getName() + "...bb");//Thread-0...bb
                                try {
                                        Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        }.start();

###24.12_多线程(守护线程)(掌握)
* setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都执行结束后,自动退出
代码演示如下:
                Thread t1 = new Thread() {
                        public void run() {
                                for(int i = 0; i < 50; i++) {
                                        System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
                                        try {
                                                Thread.sleep(10);
                                        } catch (InterruptedException e) {
                                                e.printStackTrace();
                                        }
                                }
                        }
                };
               
                Thread t2 = new Thread() {
                        public void run() {
                                for(int i = 0; i < 5; i++) {
                                        System.out.println(getName() + "...bb");
                                        try {
                                                Thread.sleep(10);
                                        } catch (InterruptedException e) {
                                                e.printStackTrace();
                                        }
                                }
                        }
                };
               
                t1.setDaemon(true);//将t1设置为守护线程,这里注意要先设置为守护线程,才能启动线程,顺序反了报错
               
                t1.start();
                t2.start();

总结:所有线程都抢占资源,非守护线程都执行结束后,守护线程自动退出,但是由于CPU切换太快了,
在守护线程退出的一定时间内容还有输出一点点

###24.13_多线程(加入线程)(掌握)
* join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续
* join(int),可以等待指定的毫秒之后继续
代码演示如下:
        final Thread t1 = new Thread() {
                public void run() {
                        for(int i = 0; i < 50; i++) {
                                System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
                                try {
                                        Thread.sleep(10);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        };
       
        Thread t2 = new Thread() {
                public void run() {
                        for(int i = 0; i < 50; i++) {
                                if(i == 2) {
                                        try {
                                                //t1.join();//插队,加入
                                                t1.join(30);//加入,有固定的时间,过了固定时间,继续交替执行
                                                Thread.sleep(10);
                                        } catch (InterruptedException e) {
                                               
                                                e.printStackTrace();
                                        }
                                }
                                System.out.println(getName() + "...bb");
                        }
                }
        };
       
        t1.start();
        t2.start();

总结:所有线程都会抢占资源的,其他地方也是,单身当符合一定条件后,加入的线程会执行完毕,在执行其他线程

###24.14_多线程(礼让线程)(了解)
* Thread.yield();让出cpu
代码演示如下:
public class Demo6_Yield {

        public static void main(String[] args) {
                new MyThread().start();
                new MyThread().start();
        }

}

class MyThread extends Thread {
        public void run() {
                for(int i = 1; i <= 1000; i++) {
                        if(i % 10 == 0) {
                                Thread.yield();//让出CPU
                        }
                        System.out.println(getName() + "..." + i);
                }
        }
}
总结:鸡肋方法,效果不太明显,了解就好

###24.15_多线程(设置线程的优先级)(了解)
* setPriority()设置线程的优先级,范围1-10,默认为5:
这个方法在一些时候会有一些鸡肋,什么意思呢,原因是线程在运行过程中除非加了同步,否则是不受控制的,
另一方面,程序的优先级是不可能被绝对执行的,因为有一个程序被绝对优先执行,相当于其他所有的程序都要停下来,
难道QQ的执行权被提高了,就要360挂掉么,不可能的,
我们的电脑一般属于并发运行,如果有一个程序可以阻止所有别的程序运行,那这个并发的过程就要出问题了,
所以其实优先级,更准确的来说只是提高了某个线程执行几率,就好像有个硬币跑起来落地后得到正面的几率为90%,
我们扔上十次,也可能十次都是反面,
所以优先级的使用,更注重于一些执行次数多,数据量大的多线程执行环境,简单的线程使用优先级是看不出来效果的
代码演示如下:
public static void main(String[] args) {
                Thread t1 = new Thread(){
                        public void run() {
                                for(int i = 0; i < 100; i++) {
                                        System.out.println(getName() + "...aaaaaaaaa" );//2还是会抢占资源
                                }
                        }
                };
               
                Thread t2 = new Thread(){
                        public void run() {
                                for(int i = 0; i < 100; i++) {
                                        System.out.println(getName() + "...bb" );//1优先一点点,但不是绝对
                                }
                        }
                };
               
                //t1.setPriority(10);//设置最大优先级
                //t2.setPriority(1);//范围1-10,默认为5
               
                t1.setPriority(Thread.MIN_PRIORITY);//设置最小的线程优先级
                t2.setPriority(Thread.MAX_PRIORITY);//设置最大的线程优先级
               
                t1.start();
                t2.start();
        }

总结:设置优先级大的线程输出的时候会优先一点点,但是后面大家还是会抢占资源,交替输出

###24.16_多线程(同步代码块)(掌握)
* 1.什么情况下需要同步
        * 当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中,CPU不要切换到其他线程工作,
          这时就需要同步,
        * 如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另外一段代码
* 2.同步代码块
        * 使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块,
        * 多个同步代码块,如果使用相同的锁对象,那么他们就是同步的
代码演示如下:
public class Demo1_Synchronized {
        public static void main(String[] args) {
                final Printer p = new Printer();
               
                new Thread() {
                        public void run() {
                                while(true) {
                                        p.print1();
                                }
                        }
                }.start();
               
                new Thread() {
                        public void run() {
                                while(true) {
                                        p.print2();
                                }
                        }
                }.start();
        }
}

class Printer {
        Demo d = new Demo();
        public void print1() {
//synchronized(new Demo()) {//同步代码块,锁机制,锁对象可以是任意的,但是被锁的代码需要保证是同一把锁,不能用匿名对象
                synchronized(d) {//可以注释掉同步代码块来演示区别
                        System.out.print("黑");
                        System.out.print("马");
                        System.out.print("程");
                        System.out.print("序");
                        System.out.print("员");
                        System.out.print("\r\n");
                }
        }
       
        public void print2() {
                //synchronized(new Demo()) {//锁对象不能用匿名对象,因为匿名对象不是同一个对象
                synchronized(d) {               
                        System.out.print("传");
                        System.out.print("智");
                        System.out.print("播");
                        System.out.print("客");
                        System.out.print("\r\n");
                }
        }
}
class Demo{}

总结:不加同步代码块两个线程抢占资源输出就会乱序如黑马传智程等,但是加了同步代码块要保证是同一个锁,如果你用了
匿名对象做各自的锁,那就不是同一把锁,还是会乱序,所以保证一个线程一个线程输出完毕,要加同步代码块且是同一把锁!

###24.17_多线程(同步方法)(掌握)
* 使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的
* 非静态的同步方法的锁对象是this,静态的同步方法的锁对象是该类的字节码对象
代码演示如下:
public class Demo2_Synchronized {
        public static void main(String[] args) {
                final Printer2 p = new Printer2();
               
                new Thread() {
                        public void run() {
                                while(true) {
                                        p.print();
//                                        p.print1();
                                }
                        }
                }.start();
               
                new Thread() {
                        public void run() {
                                while(true) {
                                        p.print0();
//                                        p.print2();
                                }
                        }
                }.start();
        }

}

class Printer2 {

        //1非静态同步函数的锁是:this,演示时用一个同步方法,一个同步代码块但是锁对象不是this,
        public synchronized void print() {//同步方法只需要在方法上加synchronized关键字即可
                System.out.print("黑");
                System.out.print("马");
                System.out.print("程");
                System.out.print("序");
                System.out.print("员");
                System.out.print("\r\n");
        }
        public void print0() {
                //synchronized(new Demo()) {//锁对象不能用匿名对象,因为匿名对象不是同一个对象
//                synchronized(Printer2.class) {//其他对象即使是字节码对象也还是乱码,改为this就不会               
                synchronized(this) {//只有改为this就不会乱序,说明和非静态同步方法是同一把锁               
                        System.out.print("传");
                        System.out.print("智");
                        System.out.print("播");
                        System.out.print("客");
                        System.out.print("\r\n");
                }
        }
       
        //2静态的同步函数的锁是:字节码对象,这个也同理控制锁对象不同演示即可
        public static synchronized void print1() {//同步方法只需要在方法上加synchronized关键字即可
                System.out.print("黑");
                System.out.print("马");
                System.out.print("程");
                System.out.print("序");
                System.out.print("员");
                System.out.print("\r\n");
        }
       
        public static void print2() {
                //synchronized(new Demo()) {//锁对象不能用匿名对象,因为匿名对象不是同一个对象
                synchronized(Printer2.class) {//只有改为字节码对象才不会乱序,说明和静态方法是同一个锁               
                        System.out.print("传");
                        System.out.print("智");
                        System.out.print("播");
                        System.out.print("客");
                        System.out.print("\r\n");
                }
        }
}

总结:非静态同步函数的锁是:this,演示时用一个同步方法,一个同步代码块但是锁对象不是this,如其他类对象d或者字节码
对象作为锁都会乱序,而用this作为锁就不会乱序,说明同步代码块用this作为锁时跟非静态同步方法的锁,是同一把锁;
同理,静态的同步函数的锁是:本类字节码对象,只有同步方法用同一个字节码对象时,才不会乱序,他们的锁是同一把锁!

###24.18_多线程(线程安全问题)(掌握)
* 多线程并发操作同一数据时,就有可能出现线程安全问题,如出现票数为负数,为0的情况
* 使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作
代码演示如下:                       
        public class Demo2_Synchronized {
                //需求:铁路售票,一共100张,通过四个窗口卖完
                public static void main(String[] args) {
                        TicketsSeller t1 = new TicketsSeller();
                        TicketsSeller t2 = new TicketsSeller();
                        TicketsSeller t3 = new TicketsSeller();
                        TicketsSeller t4 = new TicketsSeller();
                       
                        t1.setName("窗口1");
                        t2.setName("窗口2");
                        t3.setName("窗口3");
                        t4.setName("窗口4");
                        t1.start();
                        t2.start();
                        t3.start();
                        t4.start();
                }
       
        }
       
        class TicketsSeller extends Thread {
                private static int tickets = 100;//共享数据定义成静态!!!
                static Object obj = new Object();//要保证是同一把锁,引用数据类型成员变量当作锁对象,必须静态!!!
                public TicketsSeller() {
                        super();
                       
                }
                public TicketsSeller(String name) {
                        super(name);
                }
                public void run() {
                        while(true) {//锁对象还可以用Ticket.class,必须保证是同一把锁即可
                                synchronized(obj) {//并发操作同一个共享数据,用同步代码块解决线程的安全问题!!!
                                        if(tickets <= 0)
                                                break;
                                        try {//让线程睡眠,让线程安全问题更加突出,更容易出现票数为负数等情况
                                                Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
                                        } catch (InterruptedException e) {
                                               
                                                e.printStackTrace();
                                        }
                                        System.out.println(getName() + "...这是第" + tickets-- + "号票");
                                }
                        }
                }
        }
总结:统一用同步代码块所在类的字节码对象做同步锁对象即可,原因解释在下个知识点

###24.19_多线程(火车站卖票的例子用实现Runnable接口)(掌握)
代码演示如下:
public class Demo4_Ticket {
        //火车站卖100张票的例子用实现Runnable接口方式实现
        public static void main(String[] args) {
                MyTicket mt = new MyTicket();
                new Thread(mt).start();
                new Thread(mt).start();
                new Thread(mt).start();
                new Thread(mt).start();
               
                /*Thread t1 = new Thread(mt);//多次启动同一个线程是非法的,会报运行时异常
                t1.start();
                t1.start();
                t1.start();
                t1.start();*/
        }
}

class MyTicket implements Runnable {
        private int tickets = 100;//这里不用定义成静态,因为只创建一个对象,跟下面可以用this作为锁是一样的,
        @Override//当然定义成静态也没问题,为了养成共享数据定义成静态的习惯,建议定义成静态
        public void run() {
                while(true) {
                        synchronized(this) {//并发操作同一个共享数据,用同步代码块解决线程的安全问题!!!
                                if(tickets <= 0) {
                                        break;
                                }
                                try {
                                        Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
                                } catch (InterruptedException e) {
                                       
                                        e.printStackTrace();
                                }
                    System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
                        }
                }
        }
}

总结:用同步代码块时要保证是同一把锁,如果用继承线程类创建四个线程时this对象都不同,而用实现Runnable接口时,虽然
也创建了四个线程类对象,但是传入构造的都是用一个Runnable对象引用即this,所以为了避免这种麻烦,以后用同步代码块时,
锁对象最好用同步代码块所在类的字节码对象,这样可以保证是同一个锁,或者在同步代码块所在类定义一个静态成员变量,如
private static Object obj = new Object();用obj作为锁,但是麻烦,所以,统一用同步代码块所在类的字节码对象做同步锁
但是,如果用的是同步方法就要分情况了,非静态同步方法的锁对象是this,而静态同步方法的锁对象是该类的字节码对象!!!

###24.20_多线程(死锁)(了解)
* 多线程同步的时候,如果同步代码嵌套,使用相同锁,就有可能出现死锁
        * 尽量不要嵌套使用同步代码块!!!
代码演示如下:               
private static String s1 = "筷子左";
private static String s2 = "筷子右";
public static void main(String[] args) {
        new Thread() {
                public void run() {
                        while(true) {
                                synchronized(s1) {
                                        System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
                                        synchronized(s2) {
                                                System.out.println(getName() + "...拿到" + s2 + "开吃");
                                        }
                                }
                        }
                }
        }.start();
       
        new Thread() {
                public void run() {
                        while(true) {
                                synchronized(s2) {
                                        System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
                                        synchronized(s1) {
                                                System.out.println(getName() + "...拿到" + s1 + "开吃");
                                        }
                                }
                        }
                }
        }.start();
}
总结:两个线程强转资源,但我拿不到你的锁,你拿不到我的锁,而且我们两个都没有释放锁的情况下就会出现僵持的死锁状态,
谁有动弹不得,不能输出,所以为了避免这种情况,尽量不要嵌套使用同步代码块!!!

###24.21_多线程(以前的线程安全的类回顾)(掌握)
* A:回顾以前说过的线程安全问题
        * 看源码:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
        * Vector是线程安全的,ArrayList是线程不安全的
        * StringBuffer是线程安全的,StringBuilder是线程不安全的
        * Hashtable是线程安全的,HashMap是线程不安全的
总结:线程同步,线程安全,但是效率较低

###24.22_day24总结
* 把今天的知识点总结一遍:
进程和线程-多线程并发和并行的区别-Java虚拟机进程至少启动了主线程和垃圾回收线程-多线程程序的两种实现方式-
获取当前线程对象和设置获取线程名字-线程休眠,线程守护,线程加入,线程礼让,线程设置优先级-同步代码块-同步方法-
-解决线程的安全性问题-多线程死锁


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马