zoukankan      html  css  js  c++  java
  • 【Java多线程 32】

    一、概念

    1、程序(program):是为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

    2、进程(process):是程序的一次性执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生,存在和消亡的过程。----生命周期

    • 程序是静态的,进程是动态的
    • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

    3、线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。

    • 若一个进程同一时间并行支撑多个线程,就是支持多线程的
    • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
    • 一个进程中的多个线程共享相同的内存单元/内存地址空间-->他们从同一堆中分配对象,可以访问相同的变量和对象,这就使的线程间通信更简便、高效。但是多个线程操作共享的系统资源可能就会带来安全的隐患

    二、线程的创建和使用

     1、Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类实现

    2、Thread类的特性

      |-- 每个线程都是通过某个特定的Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体

      |-- 通过改Thread对象的start()方法来启动这个线程,而非直接调用run()

    ---->即两种方式创建多线程

    **************第一种********************

    package com.csii.day01;
    
    /**
     * @Author wufq
     * @Date 2020/11/17 17:33
     * 多线程的创建,方法一:继承与Thread类
     * 1、创建一个继承于Thread类的子类
     * 2、重写Thread类的run()  -->将此线程执行的操作声明在run()中
     * 3、创建Thread类的子类的对象
     * 4、通过此对象调start()方法 -->使该线程开始执行;Java虚拟机调用该线程的run()方法
     *
     * 例子:遍历100以内所有的偶数
     *
     */
    //1、创建一个继承于Thread类的子类
    class MyThread extends Thread{
    
    //    2、重写run()方法
    
        @Override
        public void run() {
            for(int i=0;i<50;i++){
                if(i%2==0){
                    System.out.println(i);
                }
            }
        }
    }
    
    public class ThreadTest {
        public static void main(String[] args){
    //        3、创建Thread类的子类对象
            MyThread  t1= new MyThread();
    
    //        4、调用start()方法
            t1.start();
    
            for(int i=0;i<50;i++){
                if(i%2==0){
                    System.out.println(i+"******main()********");
                }
            }
        }
    }
    
    
    ----------------
    既有主线程在执行,又有MyThread类的run线程子执行,所有就实现了多线程调用

    两个问题:

    问题一:我们不能通过直接调用run()的方法启动线程,要想启动多线程只能调用start方法

    调用了run()方法就相当于是单线程对象调用run方法,程序从上到下执行

    问题二:在启动一个线程,遍历100以内的偶数
    不可以让已经start()的线程去执行,会报java.lang.IllegalThreadStateException错误

    我们需要重新创建一个线程的对象,调用start方法,或者循环控制几个线程

     for(int i=0;i<=3;i++){
    
    //            3、创建Thread类的子类对象
                MyThread  t1= new MyThread();
    
    
    //        4、调用start()方法
                t1.start();
                System.out.println("=====");
    
            }

    练习题:

    package com.csii.day02;
    
    /**
     * @Author wufq
     * @Date 2020/11/18 11:25
     * 练习:创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数
     */
    public class ThreadExer {
    
        public static void main(String[] args){
            //第一种直接声明子类对象,调用start()方法
            MyThread1 m1 = new MyThread1();
            MyThread2 m2 = new MyThread2();
    
            m1.start();
            m2.start();
    
            //第二种创建Thread类的匿名子类的方式
    
            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();
    
            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{
    
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }
    
    
    class MyThread2 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2!=0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }

    ====执行结果:======
    Thread-0:0
    Thread-0:2
    Thread-0:4
    Thread-0:6
    Thread-0:8
    Thread-0:10
    Thread-0:12
    Thread-0:14
    Thread-0:16
    Thread-0:18
    Thread-0:20
    Thread-0:22
    Thread-0:24
    Thread-1:1
    Thread-1:3
    Thread-1:5
    Thread-1:7
    Thread-1:9
    Thread-1:11
    Thread-1:13
    Thread-1:15
    Thread-1:17
    Thread-1:19
    Thread-0:2

     3、线程常用方法

    1、start()启动当前线程;调用当前线程的run()
    2、run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    3、currentThread():静态方法,返回当前代码的线程
    4、getName():获取当前线程的名字
    5、setName():设置当前线程的名字
    6、yield():释放当前cpu的执行权
    7、join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。
    8、stop():已过时,当执行此方法时,强制结束当前线程
    9、sleep(long millis):让当前线程“睡眠”指定的millitime毫秒
    10、isAlive():判断当前线程是否存活

    package com.csii.day02;
    
    /**
     * @Author wufq
     * @Date 2020/11/18 15:06
     */
    public class ThreadMethod {
        public static void main(String[] args){
    
            /*
            * 给线程起名:
            * 1、setNanme()
            * 2、用子类继承父类Thread的构造方法在声明对象时命名
            * 3、给主线程命名:Thread.currentThread().setName("主线程");
            * */
            HelloThread h2 = new HelloThread("Thread-1");
    
            HelloThread h1 = new HelloThread();
    
            h1.setName("线程一");
            h1.start();
    
            //给主线程恒命名
            Thread.currentThread().setName("主线程");
    
            for(int i=0;i<50;i++){
                if(i%2 == 0 ){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
    
                /*
                * join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。
                * */
                if(i == 20){
                    try {
                        h1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    }
    
    
    class HelloThread extends Thread{
    
        @Override
        public void run() {
            for(int i=0;i<50;i++){
                if(i%2 == 0 ){
                    try {
                        sleep(1000);//让当前线程“睡眠”指定的millitime毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
    
                /*
                *yield():释放当前cpu的执行权
                **/
                if(i%20==0){
                    yield();
                }
            }
        }
    
        //用子类的构造方法命名
    
        public HelloThread(String name) {
            super(name);
        }
    
        public HelloThread(){}
    }

    4、线程优先级的设置

    Java的调度方法

    |-- 同优先级线程组成先进先出队列(先到先得服务),使用时间片策略

    |-- 对高优先级,使用有线调度的抢占式策略

    线程优先级:
    * 1、
    * MAX_PRIORITY :10
    * MIN_PRIORITY :1
    * NORM_PRIORITY : 5 -->默认的优先级
    *
    * 2、如何获取或设置当前线程的优先级
    * setPriority():设置线程的优先级
    * getPriority():获取线程的优先级
    *
    * 说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下
        被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才被执行

        (设置了优先级只是表明先执行的概率大,并不是一定先执行优先级高的线程)

    package com.csii.day02;
    
    /**
     * @Author wufq
     * @Date 2020/11/18 17:36
     *
     * 线程优先级:
     * 1、
     * MAX_PRIORITY :10
     * MIN_PRIORITY :1
     * NORM_PRIORITY : 5  -->默认的优先级
     *
     * 2、如何获取或设置当前线程的优先级
     * setPriority():设置线程的优先级
     * getPriority():获取线程的优先级
     *
     * 说明:高优先级的线程要抢占低优先级线程CPU的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下
     * 被执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才被执行
     *
     *
     */
    public class ThreadPriority {
        public static void main(String[] args){
            MyThread3 m3 = new MyThread3("分线程");
            m3.setPriority(Thread.MAX_PRIORITY);
    
            m3.start();
    
            Thread.currentThread().setName("=主线程=");
            Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    
            for(int i=0;i<50;i++){
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority()+"*"+i);
                }
            }
    
        }
    }
    
    
    class MyThread3 extends Thread{
    
        @Override
        public void run() {
            for(int i=0;i<50;i++){
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+":"+getPriority()+"*"+i);
                }
            }
        }
    
        public MyThread3(String name) {
            super(name);
        }
    }

     **************第二种********************

    定义一个类实现Runnable接口

    1、创建一个实现Runnable接口的类

    2、实现类去实现Runnable中的抽象方法:run()

    3、创建实现类对象

    4、将此对象作为参数传递到Thread类的构造器,创建Thread类的对象

    5、通过Thread类的对象调用start()

    package com.csii.day02;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @Author wufq
     * @Date 2020/11/24 14:57
     */
    public class ThreadTest1 {
        public static void main(String[] args){
    
            //3、创建实例类对象
            MyThread1 mt = new MyThread1();
            Date now = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            String start_time = dateFormat.format( now );
            System.out.println("多线程查询开始 = 》 start_time:"+start_time);
            for(int i=0;i<3;i++){
    
                //4、将此对象作为参数传入Thread类的构造器中,创建Thread类的对象
                Thread t1 = new Thread(mt);
                //5、通过Thread类的对象调用start():@1: 启动线程  @2:调用当前线程的run()--> 调用了Runable类型的target的run()方法
                t1.start();
            }
        }
    }
    // 1、创建子类继承Runable接口
     class MyThread001 implements Runnable{
    
        //2、重写run()方法
        @Override
        public void run() {
            for(int i=0;i<50;i++){
                if(i%2==0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }
    
    =====执行结果:=====
    多线程查询开始 = 》 start_time:2020/11/24 15:18:09
    Thread-1:0
    Thread-1:2
    Thread-1:4
    Thread-1:6
    Thread-1:8
    Thread-1:10
    Thread-2:0
    Thread-2:2
    Thread-2:4
    Thread-2:6
    Thread-2:8
    Thread-2:10
    Thread-2:12
    Thread-2:14
    Thread-1:12
    Thread-3:0
    Thread-3:2

    举例:多窗口卖票,总票为100张,使用Runerable接口的方式

    package com.csii.day02;
    
    /**
     * @Author wufq
     * @Date 2020/11/24 15:21
     * 多窗口卖票,总票为100张,使用Runerable接口的方式
     * 存在线程安全,待解决
     */
    public class WindowTest01 {
        public static void main(String[] args){
             //只声明了一个对象,放到了三个构造器内,
            // ticket就不需要前面加static,如果声明了多个对象,在用ticket时就必须加static,主要作用是静态变量公用
            Ticket ticket = new Ticket();
    
            /*Thread t1 = new Thread(ticket);
            Thread t2 = new Thread(ticket);
            Thread t3 = new Thread(ticket);
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
    
            t1.start();
            t2.start();
            t3.start();*/
    
            for(int i=0;i<3;i++){
                Thread t1 = new Thread(ticket);
                t1.setName("窗口"+i+" ");
                t1.start();
            }
        }
    }
    
    
    
    class Ticket 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;
                }
            }
        }
    }
    
    =====执行结果:=====
    窗口1 卖票,票号为:100
    窗口2 卖票,票号为:100
    窗口0 卖票,票号为:100
    窗口2 卖票,票号为:98
    窗口1 卖票,票号为:99
    窗口1 卖票,票号为:95
    窗口2 卖票,票号为:96
    窗口0 卖票,票号为:97
    窗口0 卖票,票号为:92
    
    --->实现了三个窗口共卖100张票,不存在重复卖票的问题(窗口1卖了第99张,其他窗口只能卖99张以后的了,98,97张)

    比较创建线程的两种方式。

    开发中:优先选择:实现Runnable接口的方式

    原因:1、实现的方式没有类的单继承性的局限性

    2、实现的方式更适合来处理多个线程共享数据的情况

    联系:public class Thread implements Runable

    相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中

  • 相关阅读:
    Nginx缓存[proxy cache、memcache]
    Nginx重写规则
    同步异步,阻塞非阻塞 和nginx的IO模型
    cookie & session
    HTTP状态码
    web简单的整体测试
    关于 如何用电脑的adb连接Mumu模拟器
    关于社保断交一个月的影响
    关于androidStudio的下载
    可以直接拿来用的android开源项目研究
  • 原文地址:https://www.cnblogs.com/frankruby/p/13919780.html
Copyright © 2011-2022 走看看