day18-IDEA的使用与多线程

  1. IDEA的使用

  2. 程序进程线程的概念

  3. 单核与多核CPU的任务执行:并行与并发

  4. 多线程的优点

  5. 创建多线程的方式一:继承Thread

  6. 线程常用方法

  7. 线程优先级的设置

  8. 例题:多窗口卖票(继承Thread方式)

  9. 创建多线程的方式二:实现Runnable接口

  10. 例题:多窗口卖票(实现Runnable接口方式)

  11. 两种创建方式的对比

 

1,IDEA的使用

 

2,程序进程线程的概念

day18-IDEA的使用与多线程

 

3,单核与多核CPU的任务执行:并行与并发

day18-IDEA的使用与多线程

 

4,多线程的优点

day18-IDEA的使用与多线程

    需要创建多线程的情景:         1,程序需要同时执行多个任务时         2,程序需要实现一些等待的任务时,比如用户输入、文件读写操作、网络操作、搜索等         3,需要一些后台运行的程序时     一个程序的执行过程如果是“一条线”,那它就是单线程的方式,如果可以用“多条线”表示,那它就可以用多线程的方式执行

 

5,创建多线程的方式一:继承Thread

package com.atguigu.java;

/**
 * 继承Thread类创建多线程的过程
 * 1,创建一个继承Thread类的子类
 * 2,重写Thread类的run()
 * 3,创建子类的对象
 * 4,对象调用start():1,启动一个线程。2,调用这个线程类中重写过的run()
 */

public class ThreadTest {

    public static void main(String[] args) { // main()是一条线程
        MyThread t1 = new MyThread();
        // t1.run(); // 在main()这条线程中执行,并没有开启新的线程
        t1.start(); // 调用Thread中的start(),开启了另一条线程,这些都是main()这条线程做的

        // t1.start(); // 想通过t1再创建一个新的线程,会报IllegalThreadStateException的错误
        // MyThread t2 = new MyThread(); // 需要创建一个新的对象来调用start(),创建新的线程
        // t2.start();

        System.out.println("hello"); // 在第一条线程中执行,两条线程是并行的,hello可能输出在
                                    // 数字的前面、中间、后面
    }
}

class MyThread extends Thread {

    public void run() { // 希望创建的这个线程要做什么事,写在run()中。在第二条线程中执行
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}
    练习

package com.atguigu.exer;

public class ThreadDemo {

    public static void main(String[] args) {
//        MyThread1 m1 = new MyThread1();
//        MyThread2 m2 = new MyThread2();
//
//        m1.start();
//        m2.start();

        new Thread() { // 通过创建Thread类的匿名子类的匿名对象来调用start(),start()调用的run()是重写过的run()
            @Override
            public void run() {
                for(int i = 0 ; i < 100 ; i++) {
                    if(i % 2 == 0)
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                for(int i = 0 ; i < 100 ; i++) {
                    if(i % 2 != 0)
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }.start();
    }
}

class MyThread1 extends Thread {

    public void run() {
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 == 0)
                System.out.println(Thread.currentThread().getName() + ":" + i);
                // 返回当前线程的名字,默认从Thread-0开始
        }
    }
}

class MyThread2 extends Thread {

    public void run() {
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 != 0)
                System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

 

6,线程常用方法

package com.atguigu.java;

/**
 * Thread类中常用方法
 * start():启动一个线程并调用当前线程的run()
 * run():子类通常需要重写Thread类中的此方法,将创建线程后要执行的操作写在此方法中
 * currentThread():静态方法。返回当前线程
 * getName():获取线程的名字
 * setName():设置线程的名字
 * yield():释放当前CPU的执行权
 * join():线程a在执行时调用了线程b的join方法,此时a进入阻塞状态,直到b执行完后,a才结束阻塞状态
 * stop():强制结束当前线程,已过时
 * sleep():让当前线程睡眠指定的毫秒数,这段时间里线程是阻塞状态
 * isAlive():判断当前线程是否存活,返回true或false
 */

public class ThreadMethodTest {

    public static void main(String[] args) {
        HelloThread h1 = new HelloThread(); // 创建对象的过程中会调用父类的构造器,父类构造器中
                                            // 将代表线程名的属性设置为“Thread+n”
                                            // 创建的是一个子类对象,Thread.currentThread()返回线程
        h1.setName("线程一"); // 在启动一个线程并执行run()之前将它改名。调用set()方法给name属性赋值
        h1.start(); // 当执行start()后,h1实际就是开启的这个线程。h1和Thread.currentThread()是同一对象

        HelloThread h2 = new HelloThread("Thread: 2"); // 通过构造器直接给name属性赋值
        h2.start();

        try {
            h2.join(); // 可能会抛异常。h1、h2、main三条线程都在执行,当执行到main的这条语句时,
                        // main被阻塞,下面的语句不再执行,必须等到h2执行完。这个过程h1和h2之间
                        // 可能也发生了切换
        }catch(InterruptedException e) {
            System.out.println(e.getMessage());
        }

        System.out.println(h2.isAlive()); // false,由于调用了h2的join(),h2先执行完

        Thread.currentThread().setName("主线程"); // 将main()代表的主线程改名
        System.out.println(Thread.currentThread().getName());
    }
}

class HelloThread extends Thread {
    @Override
    public void run() {
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 == 0)
                try {
                    sleep(1000); // 让当前线程睡1000毫秒。可能抛出异常,不能用throws的方式
                                    // 处理异常,因为父类Thread中的run()没有throws异常
                }catch(InterruptedException e) {
                    System.out.println(e.getMessage());
                }

                System.out.println(Thread.currentThread().getName() + ":" + i);

            if(i % 20 == 0) {
                yield(); // 当该线程正在执行并且i被20整除时,调用yield()使CPU切换当前线程,所以
                        // 下一个被执行的线程可能是任何一个,包括该线程自己
            }
        }
    }

    public HelloThread() {

    }

    public HelloThread(String name) {  // 父类中的重载构造器
        super(name);
    }
}

 

7,线程优先级的设置

package com.atguigu.java;

/**
 * 线程优先级
 * MAX_PRIORITY:10  MIN_PRIORITY:1  NORM_PRIORITY:5(默认优先级)
 * getPriority():获取当前线程的优先级
 * setPriority():设置当前线程的优先级
 */

public class ThreadMethodTest1 {

    public static void main(String[] args) {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // 将主线程优先级设置为最低,要设置为1、5、10外的其他优先级时直接写数字即可
        HelloThread1 h1 = new HelloThread1();
        h1.setPriority(Thread.MAX_PRIORITY); // 将分线程优先级设置为最高。不是优先级高的就一定先执行完,而是优先级高的先执行的概率大于优先级低的
        h1.start();

        System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
    }
}

class HelloThread1 extends Thread {
    @Override
    public void run() {
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 == 0)
                System.out.println(getName() + ":" + getPriority() + ":" + i); // 省略了this.,this可以指h1也可以指Thread.currentThread()
        }
    }
}

 

8,例题:多窗口卖票(继承Thread方式)

package com.atguigu.java;

/**
 * 存在线程安全问题,有可能多个线程取到同一个ticket值
 */

public class WindowTest {

    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Window extends Thread {

    private static int ticket = 100;

    @Override
    public void run() {
        while(true) {
            if(ticket > 0) {
                System.out.println(getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}

 

9,创建多线程的方式二:实现Runnable接口

package com.atguigu.java;

/**
 * 实现Runnable接口的方式创建多线程的过程
 * 1,创建一个实现了Runnable接口的类,并在该类中实现接口中的抽象方法run()
 * 2,创建实现类的对象,并将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 * 3,通过Thread类的对象调用start()
 */

public class ThreadTest1 {

    public static void main(String[] args) {
        MThread mThread = new MThread();
        Thread t1 = new Thread(mThread);
        t1.setName("线程一");
        t1.start(); // 开启了t1这个线程。调用的是Thread类的start(),接着调用的是当前线程的run(),也
                    // 就是Thread的run(),查看源码,MThread赋值的属性叫target,Thread的run()调用
                    // 的就是target.run(),所以最终调用的是重写后的run()
        Thread t2 = new Thread(mThread);
        t2.setName("线程二");
        t2.start(); // 开启第二个线程
    }
}

class MThread implements Runnable {
    @Override
    public void run() {
        for(int i = 0 ; i < 100 ; i++) {
            if(i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i); // 没有继承Thread类,就没有getName()等方法
            }
        }
    }
}

 

10,例题:多窗口卖票(实现Runnable接口方式)

package com.atguigu.java;

/**
 * 存在线程安全问题,有可能多个线程取到同一个ticket值
 */

public class WindowTest1 {

    public static void main(String[] args) {
        Window1 w = new Window1();

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

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start(); // 不用将ticket定义为static,因为只创建了一个对象,不论是哪个start(),最终调用的都是w.run()
    }
}

class Window1 implements  Runnable {
    private int ticket = 100;
    @Override
    public void run() {
        while(true) {
            if(ticket > 0) {
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}

 

11,两种创建方式的对比

    开发中优先选择实现Runnable接口的方式创建多线程     原因:1,如果有要继承某个父类的需求,这是不矛盾的。而继承Thread类的方式使得子类不能继承其他的类                2,以上面卖票的两种方式来看,实现Runnable接口的方式更适合多个线程共享数据,因为只需要创建一个实现类的对象     联系:Thread类也实现了Runnable接口,也就实现了run()。继承Thread的方式又重写了run(),实现Runnable接口则是实现了run()

上一篇:天亮--DAY18


下一篇:linux学习day18笔记