• 尚学堂--线程


    本章内容:

    • 线程的基本概念
    • 线程的创建和启动
    • 线程的调度和优先级
    • 线程的状态控制
    • 线程同步

    1、线程基本概念:

    线程:一个程序里面不同的执行路径(main()方法叫主线程)。

    进程:只是一个静态的概念,例如:机器上的一个.class文件,机器上的一个.exe文件这个叫一个进程

    多线程: 在同一个时间点上,一个CPU只能支持一个线程在执行

    程序执行过程:

      把程序的代码放到内存(代码区)里面,放进去之后并没开始执行,这个时候说明一个进程已经产生了,

    但还没开始执行,这叫一个进程。进程是一个静态的概念,平时所说的进程的执行说的是进程里面主线程

    (main方法)开始执行。进程是一个静态概念,实际上机器里面运行的都是线程。

    CPU的执行是这样的:

      CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上,CPU只有一个线程在运行。什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。

    2、线程的创建和启动

    • java的线程是通过java.lang.Thread类来实现的
    • VM启动时会有一个由主方法(public static void main() {} )所定义的线程
    • 可以通过创建Thread的实例来创建新的线程
    • 每个线程都是通过某个特定的Thread对象所对应的方法run()来完成操作的,方法run()称为线程体
    • 通过调用Thread类的start()方法来启动一个线程

    创建一个新的线程:

    • 实现Runnable接口
    • 继承Thread类

    实现Runnable接口:(Runnable接口只有一个run方法,重写run方法后,需要调用Thread中的start()方法就必须new一个Thread出来,并把线程类对象作为实参传入)

    public class Test {
            public static void main(String[] args) {
                    MyRunner mr = new MyRunner();//new一个线程类的对象
                    //mr.run();  方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法
                    Thread th = new Thread(mr);//要启动一个线程类就必须new一个Thread对象出来,调用Thread(Runnable target)构造方法
                    th.start();//启动新开辟的线程,新线程执行的是run()方法,新线程与主线程并行执行
    
                    for(int i = 0; i < 100; i ++) {
                            System.out.println("MainThread ------ " + i);
                    }        
            }
    }
    //定义一个类来实现Runnable接口,实现Runnable接口就表示这个类是一个线程类
    class MyRunner implements Runnable {
          public void run() {
            for(int i = 0; i < 100; i ++) {
                    System.out.println("MyRunner : " + i);
            }
          }  
    }

     继承Thread类:(Thread的子类要重写run方法,然后调用Thread的start方法启动新线程)

    public class Test {
            public static void main(String[] args) {
                    MyRunner mr = new MyRunner();
                    mr.start();//调用start()方法启动新开辟的线程
                    for(int i=0; i<=100; i++) {
                            System.out.println("MainThread ----" + i);
                    }
            }
    }
    /*
    MyRunner类从Thread类继承,通过实例化MyRunner的一个对象就能开辟一个新的线程,
    调用从Thread类继承来的start()方法就可以启动新开辟的线程
    */
    class MyRunner extends Thread {
            public void run() {
                    for(int i=0; i<=100; i++) {
                            System.out.println("MyRunner : " + i);
                    }
            }
    }

      使用实现Runnable接口和继承Thread类这两种开辟新线程的方法的选择应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程

    3、线程的调度和优先级

    isAlive:就绪、运行、阻塞状态表示还“活”着,终止、new出来没有启动也是“死”的

    4、线程的状态控制

    sleep方法实例:

    import java.util.*;//包中存放了Date()方法
    
    public class Test {
        public static void main(String[] args) {
            MyRunner mr = new MyRunner();
            Thread tr = new Thread(mr);
            tr.start();//start自动调用run()方法开始新的进程
            
            try {
                Thread.sleep(10000);//sleep为静态方法,可以通过类直接调用,main进程暂停执行10s
            } catch(InterruptedException ae) {
                // no 
            }
            tr.interrupt();
        }
    }
    
    class MyRunner implements Runnable {
        public void run() {
            while(true) {
                System.out.println("====" + new Date() + "====");
                try {
                    Thread.sleep(1000);//为什么用Thread.sleep,不能用MyRunner.sleep()
                } catch(InterruptedException ae) {
                    return; //进程停止,不使用return,会一直输出时间,不停止
                }
                
            }
        }
    }

    john方法:

    public class Test {
        public static void main(String[] args) {
            MyRunner mr = new MyRunner();
            Thread tr = new Thread(mr);
            tr.start();
    
            try {
                tr.join();//效果相当于方法调用
            } catch(InterruptedException ae) {
                //
            }
    
            for(int i=1; i<=10; i++) {
                    System.out.println("i am main Thread");
                }    
        }
    }
    
    class MyRunner implements Runnable {
        public void run() {
            for(int i=0; i<=10; i++) {
                System.out.println("i am" + i);
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException ae) {
                    return;
                }
            }
        }
    }

    yield方法:

    线程的优先级:

    public class Test {
        public static void main(String[] args) {
            Thread t1 = new Thread(new T1());
            Thread t2 = new Thread(new T2());
            t1.setPriority(Thread.NORM_PRIORITY + 3);
            t1.start();
            t2.start();
            //因为是多线程,"t1的优先级:" + t1.getPriority() 语句不一定是在最后输出
            System.out.println("t1的优先级:" + t1.getPriority());    
        }
    }
    
    class T1 implements Runnable {
        public void run() {
            for(int i=0; i<=10; i++) {
                System.out.println("-----T1  " + i);
            }
        }
    }
    
    class T2 implements Runnable {
        public void run() {
            for(int i=0; i<=10; i++) {
                System.out.println("-----T2  " + i);
            }
        }
    }

    5、线程同步

    public class Test implements Runnable {
        Timer test = new Timer();
        public static void main(String[] args) {
            Test t = new Test();//new线程类对象
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.setName("t1");
            t2.setName("t2");
            t1.start();
            t2.start();
        }
        public void run() {
            test.add(Thread.currentThread().getName());
        }
    }
    
    class Timer {
        private static int num = 0;
        public void add(String name) {  
                num ++;
                try {
                    Thread.sleep(1);//即使不写sleep,第一个线程执行过程中也很可能被第二个线程打断。Thread.sleep(1)只是为了放大这个效果
                } catch(InterruptedException e) {
                    // no
                }
                System.out.println(name + ",你是第" + num + "个使用timer的线程");
        }
    }
    View Code

    防止一个线程在执行过程中被另外的线程打断:
    • synchronized 声明方法
    • 将代码写在synchronized(this){}方法体中
    public class Test implements Runnable {
        Timer test = new Timer();
        public static void main(String[] args) {
            Test t = new Test();//new线程类对象
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.setName("t1");//改变线程名称,使之与参数 name 相同
            t2.setName("t2");
            t1.start();//调用run()方法
            t2.start();
        }
        public void run() {
            test.add(Thread.currentThread().getName());
        }
    }
    
    class Timer {
        private static int num = 0;
        public synchronized void add(String name) {// 在声明方法时加入synchronized时表示在执行这个方法的过程之中当前对象被锁
            //synchronized(this) {  锁定当前对象(一个线程在执行方法体语句过程中不会被另外一个线程打断)
                num ++;
                try {
                    Thread.sleep(1);//即使不写sleep,第一个线程执行过程中也很可能被第二个线程打断。Thread.sleep(1)只是为了放大这个效果
                } catch(InterruptedException e) {
                    // no
                }
                System.out.println(name + ",你是第" + num + "个使用timer的线程");
            //}
        }
    }

     线程死锁:

    /*这个小程序模拟的是线程死锁的问题*/
    public class TestDeadLock implements Runnable {
        public int flag = 1;
        static Object o1 = new Object(), o2 = new Object();
    
        public void run() {
            System.out.println(Thread.currentThread().getName() + "的flag=" + flag);
            /*
             * 运行程序后发现程序执行到这里打印出flag以后就再也不往下执行后面的if语句了 
             * 程序也就死在了这里,既不往下执行也不退出
             */
    
            /* 这是flag=1这个线程 */
            if (flag == 1) {
                synchronized (o1) {
                    /* 使用synchronized关键字把对象01锁定了 */
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {
                        /*
                         * 前面已经锁住了对象o1,只要再能锁住o2,那么就能执行打印出1的操作了 
                         * 可是这里无法锁定对象o2,因为在另外一个flag=0这个线程里面已经把对象o1给锁住了 
                         * 尽管锁住o2这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o2不放的
                         */
                        System.out.println("1");
                    }
                }
            }
            /*
             * 这里的两个if语句都将无法执行,因为已经造成了线程死锁的问题 
             * flag=1这个线程在等待flag=0这个线程把对象o2的锁解开, 
             * 而flag=0这个线程也在等待flag=1这个线程把对象o1的锁解开 
             * 然而这两个线程都不愿意解开锁住的对象,所以就造成了线程死锁的问题
             */
    
            /* 这是flag=0这个线程 */
            if (flag == 0) {
                synchronized (o2) {
                    /* 这里先使用synchronized锁住对象o2 */
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {
                        /*
                         * 前面已经锁住了对象o2,只要再能锁住o1,那么就能执行打印出0的操作了 可是这里无法锁定对象o1,因为在另外一个flag=1这个线程里面已经把对象o1给锁住了 尽管锁住o1这个对象的线程会每隔500毫秒睡眠一次,可是在睡眠的时候仍然是锁住o1不放的
                         */
                        System.out.println("0");
                    }
                }
            }
        }
    
        public static void main(String args[]) {
            TestDeadLock td1 = new TestDeadLock();
            TestDeadLock td2 = new TestDeadLock();
            td1.flag = 1;
            td2.flag = 0;
            Thread t1 = new Thread(td1);
            Thread t2 = new Thread(td2);
            t1.setName("线程td1");
            t2.setName("线程td2");
            t1.start();
            t2.start();
        }
    }
  • 相关阅读:
    【NOIP】OpenJudge
    【NOIP】OpenJudge
    【NOIP】OpenJudge
    1.1编程基础之输入输出-10:超级玛丽游戏
    1.1编程基础之输入输出-09:字符菱形
    1.1编程基础之输入输出-08:字符三角形
    1.1编程基础之输入输出-06:空格分隔输出
    1.1编程基础之输入输出-07:输出浮点数
    1.1编程基础之输入输出-04:输出保留3位小数的浮点数
    1.1编程基础之输入输出-05:输出保留12位小数的浮点数
  • 原文地址:https://www.cnblogs.com/wmjlh/p/7237100.html
Copyright © 2020-2023  润新知