今天复习一下多线程

1.什么是线程
     线程是程序执行的一条路径, 一个进程中可以包含多条线程
     多线程并发执行可以提高程序的效率, 可以同时完成多项工作
    1.1 多线程的应用场景
         迅雷开启多条线程一起下载
         QQ同时和多个人一起视频
         服务器同时处理多个客户端请求

    1.2 多线程并行和并发的区别(了解)
        并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
        并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两 个任务都在运行。
         比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
         如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

     1.3 Java程序运行原理
        Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” 然后主线程去调用某个类的 main 方法。
     1.4 JVM的启动是多线程的吗 (JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的)

2 多线程程序实现的方式(掌握)
    2.1 方式1 继承Thread类

     定义类继承Thread
     重写run方法
     把新线程要做的事写在run方法中
     创建线程对象
     开启新线程, 内部会自动执行run方法

public class Demo1 {
    public static void main(String[] args) {
        myThread my = new myThread(); //4,创建Thread类的子类对象
        my.start();                   //5,开启线程  

        for (int i = 0; i < 1000; i++) {
            System.out.println(i + "AA");
        }
    }
}

class myThread extends Thread {           //1,定义类继承Thread
    @Override
    public void run() {                   // 2,重写run方法
        for (int i = 0; i <= 1000; i++) { // 3 将要执行的代码写在run方法中
            System.out.println(i + "BB");
        }
    }
}

2.2 方式2 实现Runnable接口
     定义类实现Runnable接口
     实现run方法
     把新线程要做的事写在run方法中
     创建自定义的Runnable的子类对象
     创建Thread对象, 传入Runnable
     调用start()开启新线程, 内部会自动调用Runnable的run()方法

public class Demo2 {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable(); // 4 创建Runnable的子类对象
        //Runnable target = new MyRunnable(); 
        Thread t = new Thread(mr);        // 5 将其当做参数传递给Thread的构造方法
        t.start();                        // 6 开启线程
        for (int i = 0; i < 1000; i++) {
            System.out.println(i + " BBBBB");
        }

    }
}
class MyRunnable implements Runnable {   // 1 定义一个类实现Runnable
    @Override
    public void run() {                  // 2,重写run方法
        for (int i = 0; i < 1000; i++) { // 3 将要执行的代码写在run方法中
            System.out.println(i + " AAAAAAAAAA");
        }
    }
}

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

2.4  查看源码观察两种实现方式的区别:
    继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
    实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

2.5 两种方式优缺点

继承Thread (开发常用)
     好处是:可以直接使用Thread类中的方法,代码简单
     弊端是:如果已经有了父类,就不能用这种方法
 实现Runnable接口
     好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
     弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

3 匿名内部类实现线程的两种方式(掌握)

3.1 继承Thread类

public class Demo3_1 {
    public static void main(String[] args) {
        new Thread() { // 1 继承Thread类
            public void run() { // 2 重写run方法
                for (int i = 0; i < 1000; i++) { // 3 将所要执行的代码写到run方法中
                    System.out.println(i + " AAAAA");
                }
            }
        }.start(); // 4 开启线程

        for (int i = 0; i < 1000; i++) {
            System.out.println(i + " BBBBB");
        }
    }
}

3.2 实现Runnable接口

public class Demo3_2 {
    public static void main(String[] args) {
        new Thread(new Runnable() { // 1 将Runnable的子类对象传递给Thread的构造方法
            @Override
            public void run() { // 2 重写run方法
                for (int i = 0; i < 1000; i++) { // 3 将要执行代码写在run方法中
                    System.out.println(i + " AAAAA");
                }
            }
        }).start();// 4 开启线程

        for (int i = 0; i < 1000; i++) {
            System.out.println(i + " BBBB");
        }
    }
}

4 常用方法

4.1 获取名字和设置名字

继承Thread并通过构造方法设置名字 用getName()可以获取名字

public class Demo1_Name {
    public static void main(String[] args) {
        // 1 通过构造方法设置名字 直接在构造方法里传入一个字符串的参数
        new Thread("小白") {
            public void run() {
                System.out.println(this.getName() + " AAAAAAAAA");
            }
        }.start();

        // 2 通过setName("名字") 方法设置方法
        new Thread() {
            public void run() {
                this.setName("小黑");
                System.out.println(this.getName() + " BBBBBBBBB");
            }
        }.start();
    }
}

4.2 获取当前线程的对象 Thread.currentThread()

public class Demo_CurrentThtead {
    public static void main(String[] args) {
        // 1  直接继承Thread方式创建线程
        new Thread("直接继承Thread方式创建线程") {
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();

        // 2  实现Runnable方式创建线程
        new Thread(new Runnable() {
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }, "实现Runnable方式创建线程").start();

        // 3  获取主函数线程的引用,并改名字
        Thread.currentThread().setName("我是主线程");
        System.out.println(Thread.currentThread().getName());
    }
}

4.3 sleep() 休眠线程(掌握)
 Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 = 1000000000

public class Demo4_Sleep {
    public static void main(String[] args) throws InterruptedException { // 线程休眠 抛出一个中断异常
        for (int i = 20; i > 0; i--) {
            Thread.sleep(1000); // 主线程每循环一次睡眠一秒
            System.out.println("倒计时" + i);
        }
    }
}

4.4 setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出

(比如下象棋,当帅(非守护线程)死了之后,那么这局棋也就结束了 其他小兵也就挂了(守护线程)。)

创建线程t1与t2 正常情况下 t1输出2次AAAAA t2输出50次BBBBB

把t2设置成守护线程之后 只要t1执行完两次AAAAA之后 t2就会自动结束 不会把50次BBBBB执行完

public class Demo4_Daemon {
    public static void main(String[] args) {
        // 创建线程t1 输出两次 AAAAA
        Thread t1 = new Thread(){
            public void run(){
                for ( int i = 0 ;i<2;i++){
                    System.out.println(getName()+"AAAAAAAAAA");
                }
            }
        };

        // 创建线程t2 输出50次 BBBBB
        Thread t2 = new Thread(){
            public void run(){
                for ( int i = 0 ;i<2;i++){
                    System.out.println(getName()+"BB");
                }
            }
        };

        // 设置t2为守护线程 当传入true就是意味着设置为守护线程
        t2.setDaemon(true);
        // 启动线程
        t1.start();
        t2.start();
    }
}

4.5 join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
      join(int), 可以等待指定的毫秒之后继续

创建t1,t2两个线程 分别输出10次 当t2输出两次之后 将t1插入 优先执行

public class Demo5_Join {
    public static void main(String[] args) {
        // 创建线程 t1 为了使t1可以给匿名内部类直接调用 将t1设置为final
        final Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(getName() + "AAAAA");
                }
            }
        };

        // 创建线程 t2
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 10; i++) {
                    // 如果 i ==2 将t1线程插入 优先执行
                    if (i == 2) {
                        try {
                            // 注意 如果在匿名内部类里面直接调用局部变量 那这个局部变量必须是final的
                            t1.join();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(getName() + "BBBBB");
                }
            }
        };

        // 启动线程
        t1.start();
        t2.start();
    }
}

4.6 礼让线程(了解) yield()让出cpu

玄学:执行有滞留时间 效果不是很明显

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

class MyThread extends Thread {
    // 当i能被十整除的时候 让出线程
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 10 == 0) {
                Thread.yield();
            }
            System.out.println(getName() + "......" + i);
        }
    }
}

4.7 setPriority()设置线程的优先级 (最小是1 默认是5 最大是10)

有一点效果 观察不是很明显

public class Demo7_Priority {
    public static void main(String[] args) {
        // 创建线程 t1
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(getName() + ".......AAA");
                }
            }
        };
        
        // 创建线程 t2
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(getName() + "....BBB");
                }
            }
        };

        // 设置线程t1的优先级为最小 1
        t1.setPriority(Thread.MIN_PRIORITY);
        // 设置线程t2的优先级为最大 10
        t2.setPriority(Thread.MAX_PRIORITY);

        // 启动线程
        t1.start();
        t2.start();
    }
}

5 同步代码块
       什么情况下需要同步
          当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
          如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
       同步代码块
          使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
          多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

5.1 同步代码块 案例示范

没加锁之前print1()里面的打印 有可能没执行完 就会被切换到 print2() 这样子 一句话就乱掉了

加锁之后print2()想要执行 就只能等peint()1执行完毕才能抢到线程了

public class Demo_Synchronized {
    public static void main(String[] args) {
        final Printer p1 = new Printer();
        new Thread() {
            public void run() {
                while (true) {
                    p1.print1();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while (true) {
                    p1.print2();
                }
            }
        }.start();
    }
}

class Printer {

    public void print1() {
        synchronized (this) { //同步代码块,锁机制,锁对象可以是任意的
            System.out.print("我");
            System.out.print("话");
            System.out.print("还");
            System.out.print("没");
            System.out.print("说");
            System.out.print("完\n");
        }
    }

    public void print2() {
        synchronized (this) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
            System.out.print("\n请");
            System.out.print("不");
            System.out.print("要");
            System.out.print("切");
            System.out.print("换");
            System.out.print("线");
            System.out.print("程\n");
        }
    }
}

5.2  使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的

疑问 非静态的同步方法的锁对象是什么 (是this)

静态方法的同步方法的锁对象是什么(是该类的字节码对象 类.class)

public class Demo2_Synchronized {
    public static void main(String[] args) {
        final Printer2 p1 = new Printer2();
        new Thread() {
            public void run() {
                while (true) {
                    p1.print1();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while (true) {
                    p1.print2();
                }
            }
        }.start();
    }
}

class Printer2 {
    public synchronized void print1() { // 同步方法只需要在方法上加上synchronized关键字即可
        System.out.print("我");
        System.out.print("话");
        System.out.print("还");
        System.out.print("没");
        System.out.print("说");
        System.out.print("完\n");
    }
    
    public void print2() {
        synchronized (this){ //锁对象不能用匿名对象,因为匿名对象不是同一个对象
            System.out.print("\n请");
            System.out.print("不");
            System.out.print("要");
            System.out.print("切");
            System.out.print("换");
            System.out.print("线");
            System.out.print("程\n");
        }
    }
}

6 线程安全问题
 多线程并发操作同一数据时, 就有可能出现线程安全问题
 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

6.1 小练习 需求:铁路售票,一共100张,通过四个窗口卖完.  (Thread实现)

注意: 1 因为有四条线程 所以变量(票数)得是静态的共享变量

            2 当一条线程对 当前变量进行操作时 得对这些操作加上同步代码块 不然变量会乱掉

            3 静态代码块 锁对象必须得是同一个 我们可以传类的字节码对象 字节码对象只有一份

public class Demo_Ticket {
    public static void main(String[] args) {

        // 创建4个线程并启动
        new Ticket().start();
        new Ticket().start();
        new Ticket().start();
        new Ticket().start();

    }
}

class Ticket extends Thread {
    // 设置一个静态共享变量
    private static int ticket = 100;

    public void run() {
        while (true) {
            // 加一个同步代码块 是当前Ticket只可以被当前线程进行操作
            // 注意 锁对象得是同一份  (Ticket.class) 是Ticket类的字节码对象只有一份
            synchronized (Ticket.class) {
                if (ticket <= 0) {
                    break;
                }
                try {
                    Thread.sleep(10); // 为了使效果更明显 使线程们睡眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 打印票数
                System.out.println(getName() + "......这是第" + ticket-- + "号票");
            }
        }
    }
}

6.2 小练习 需求:铁路售票,一共100张,通过四个窗口卖完.  (Runnable接口实现)

public class Demo4_Ticket {
    public static void main(String[] args) {
        MyTicket mt = new MyTicket();
        // 创建四条线程 传入 Runnable 接口的实例
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

class MyTicket implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while (true) {
            synchronized (MyTicket.class) {
                if (tickets <= 0) {
                    break;
                }
                try {
                    Thread.sleep(10); // 为了使效果更明显 使线程们睡眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 打印票数
                System.out.println(Thread.currentThread().getName() + "......这是第" + tickets -- + "号票");
            }
        }
    }
}

6.3 死锁(了解)
 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁 (尽量不要嵌套使用)

public class Demo_DeadLock {
    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();
    }
}

 6.4 回想以前说过的线程安全问题

Vector是线程安全的,ArrayList是线程不安全的

StringBuffer是线程安全的,StringBuilder是线程不安全的

Hashtable是线程安全的,HashMap是线程不安全的

7 线程间的通信
     7.1.什么时候需要通信
         多个线程并发执行时, 在默认情况下CPU是随机切换线程的
         如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
     7.2.怎么通信
         如果希望线程等待, 就调用wait()
         如果希望唤醒等待的线程, 就调用notify();
         这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用

7.3     两个线程间的通信

public class Damo1_Notify {
    public static void main(String[] args) {
        Println p = new Println();
        new Thread() {
            public void run() {
                while (true) {
                    try { // 因为在run方法中 父类没有抛异常 所以子类必须处理
                        p.print1();
                    } catch (InterruptedException e) {
                    }
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while (true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                    }
                }
            }
        }.start();
    }
}

class Println {
    private int flag = 1; // 设置开关
    public void print1() throws InterruptedException { // 线程被暂停会抛 InterruptedException异常
        synchronized (this) { // 设置同步代码块 锁对象是调用者
            if (flag != 1) { // 如果开关不为1
                this.wait(); // 线程等待
            }
            // 开关为1或等待完毕 输出“开始”
            System.out.print("开");
            System.out.println("始");
            // 把开关设置为 2
            flag = 2;
            // 叫醒线程
            this.notify();
        }
    }

    public void print2() throws InterruptedException {  // 线程被暂停会抛 InterruptedException异常
        synchronized (this) { // 设置同步代码块 锁对象是调用者
            if (flag != 2) { // 如果开关不为2
                this.wait();  // 线程等待
            }
            // 开关为2或等待完毕 输出“开始”
            System.out.print("暂");
            System.out.println("停");
            // 把开关设置为 2
            flag = 1;
            // 叫醒线程
            this.notify();
        }
    }
}

7.4  多线程(三个或三个以上间的线程通信)
 多个线程通信的问题
     notify()方法是随机唤醒一个线程
     notifyAll()方法是唤醒所有线程
     JDK5之前无法唤醒指定的一个线程
     如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件

public class Demo1_Notify {
    public static void main(String[] arge) {
        // 实例化 Println2
        Println2 p = new Println2();

        // 创建3条线程 分别调用 p的Println1()  Println2()  Println3()
        // 注意 这里要处理一下异常 
        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.print1();
                    }
                } catch (InterruptedException e) {

                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.print2();
                    }
                } catch (InterruptedException e) {
                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.ptint3();
                    }
                } catch (InterruptedException e) {

                }
            }
        }.start();

    }
}

class Println2 {
    private static int falg = 1; // 设置开关
   //注意 在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法

    public void print1() throws InterruptedException {
        synchronized (this) { // 同步锁 传入调用者对象
            while (falg != 1) { // 不是第一个则等待
                this.wait();
            }
            // 是第一个则输出A1A2后 将开关改为二 然后叫醒所有等待的线程
            System.out.print("A1");
            System.out.println("A2");
            falg = 2;
            this.notifyAll();
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) { // 同步锁 传入调用者对象
            while (falg != 2) {  // 不是第二个则等待
                this.wait();
            }
            // 是第二个则输出B1B2后 将开关改为三 然后叫醒所有等待的线程
            System.out.print("B1");
            System.out.println("B2");
            falg = 3;
            this.notifyAll();
        }
    }

    public void ptint3() throws InterruptedException {
        synchronized (this) {  // 同步锁 传入调用者对象
            while (falg != 3) {  // 不是第三个则等待
                this.wait();
            }
            // 是第三个则输出C1C2后 将开关改为三 然后叫醒所有等待的线程
            System.out.print("C1");
            System.out.println("C2");
            falg = 1;
            this.notifyAll();
        }
    }
}

7.5 疑问

为什么wait方法和notify方法要定义在Object这个类中

    因为锁对象可以是任意对象,而Object是所有对象的基类,所以wait方法和notify方法需要定义在Object这个类中

sleep方法和wait方法的区别

    sleep方法必须传入参数,参数就是时间,时间到了自动醒来

    wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待。

    sleep方法在同步函数或同步代码块中不释放锁,睡着期间也是抱着锁睡

    wait方法在同步函数或者同步代码块中,释放锁

8 JDK1.5的新特性互斥锁
 1.同步
     使用ReentrantLock类的lock()和unlock()方法进行同步
 2.通信
     使用ReentrantLock类的newCondition()方法可以获取Condition对象
     需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
     不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Demo1_Notify {
    public static void main(String[] arge) {
        // 实例化 Println2
        final Println2 p = new Println2();

        // 创建3条线程 分别调用 p的Println1()  Println2()  Println3()
        // 注意 这里要处理一下异常
        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.print1();
                    }
                } catch (InterruptedException e) {

                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.print2();
                    }
                } catch (InterruptedException e) {
                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        p.ptint3();
                    }
                } catch (InterruptedException e) {

                }
            }
        }.start();

    }
}

class Println2 {
    // 创建一个锁
    private ReentrantLock r = new ReentrantLock();

    // 创建三个监视器
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();

    private static int falg = 1; // 设置开关

    public void print1() throws InterruptedException {
        // 获取锁
        r.lock();
        if (falg != 1) { // 不是第一个则监视器c1等待
            c1.await();
        }
        // 是第一个则输出A1A2后 将开关改为二 然后叫醒c2
        System.out.print("A1");
        System.out.println("A2");
        falg = 2;
        c2.signal();
        // 释放锁
        r.unlock();
    }


    public void print2() throws InterruptedException {
        // 获取锁
        r.lock();
        if (falg != 2) {  // 不是第二个则监视器c2等待
            c2.await();
        }
        // 是第二个则输出B1B2后 将开关改为三 然后叫醒c3
        System.out.print("B1");
        System.out.println("B2");
        falg = 3;
        c3.signal();
        // 释放锁
        r.unlock();
    }


    public void ptint3() throws InterruptedException {
        // 获取锁
        r.lock();
        if (falg != 3) {  // 不是第三个则监视器c3等待
            c3.await();
        }
        // 是第三个则输出C1C2后 将开关改为三 然后叫醒c1
        System.out.print("C1");
        System.out.println("C2");
        falg = 1;
        c1.signal();
        // 释放锁
        r.unlock();
    }
}

9 线程组的概述和使用

  线程组概述
     Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
     默认情况下,所有的线程都属于主线程组。
         public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
         public final String getName()//通过线程组对象获取他组的名字
     我们也可以给线程设置分组
         1,ThreadGroup(String name) 创建线程组对象并给其赋值名字
         2,创建线程对象
         3,Thread(ThreadGroup?group, Runnable?target, String?name)
         4,设置整组的优先级或者守护线程

9.1 线程组的使用,默认是主线程组

public class Demo_ThreadGroup {
    public static void main(String[] args) {
        // demo1();
        ThreadGroup tg = new ThreadGroup("我是一个新的线程组"); // 创建新的线程组
        MyRunnable mr = new MyRunnable(); // 创建Runnable的子类对象

        Thread t1 = new Thread(tg, mr, "线程1"); // 将线程t1放在组中
        Thread t2 = new Thread(tg, mr, "线程2"); // 将线程t2放在组中

        System.out.println(t1.getThreadGroup().getName()); // 获取组名
        System.out.println(t2.getThreadGroup().getName()); // 获取组名
    }

    public static void demo1() {
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr, "线程1");
        Thread t2 = new Thread(mr, "线程2");

        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();

        System.out.println(tg1.getName());
        System.out.println(tg2.getName());
    }
}

class MyRunnable implements Runnable {
    public void run() {
    }
}

10 线程的五种状态
  新建,就绪,运行,阻塞,死亡

11 线程池概述
     程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
    内置线程池的使用概述
     JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
         public static ExecutorService newFixedThreadPool(int nThreads)
         public static ExecutorService newSingleThreadExecutor()
        这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
         Future<?> submit(Runnable task)
         <T> Future<T> submit(Callable<T> task)
     使用步骤:
         创建线程池对象
         创建Runnable实例
         提交Runnable实例
         关闭线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo_Executors {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);  // 创建线程池
        // 匿名内部类创建线程 将线程放到池子里执行
        pool.submit(new Thread() {
            public void run() {
                System.out.println("AA");
            }
        });

        // 匿名内部类创建线程 将线程放到池子里执行
        pool.submit(new Thread() {
            public void run() {
                System.out.println("BB");
            }
        });
        
        pool.shutdown(); // 关闭线程池
    }
}

猜你喜欢

转载自blog.csdn.net/a13085/article/details/81611987