zoukankan      html  css  js  c++  java
  • java基础知识回顾之java Thread类java线程实现常见的两种方式(一)

    java基础知识回顾之java Thread类--java线程实现常见的两种方式(一)

     

    创建线程的第一种方式:
           / * 步骤:
             *     1.继承Thread类
             *     2. 重写Thread的run方法
             *         目的:将自定义的代码存储在run方法中,运行自定义线程
             *         start 1.启动线程,2调用run方法
             * 为什么要覆盖run方法?
         *       Thread类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法
         * 也就是说Thread中的run方法,用于存储线程要运行的代码。
         * 主线程要运行的方法存放在main里面,这是虚拟机定义的
         * 虚拟机开启主线程,也就是运行main方法里面的代码:
         *
        
         *
         */

    代码演示:

         *发现运行结果每一次都不同,因为多个线程都在获取CPU的使用权,cpu执行到谁,谁就运行,明确
         * 一点,cpu在某一个时刻,只能运行一个程序(多核除外),CPU在做快速的切换,达到看上去是同时运行的
         * 多线程的运行行为在互相的抢夺CPU的执行权。
         * 这是多线程的一个特性:随机性

    复制代码
    public class HelloThread extends Thread {
        private String name;
        public HelloThread(String name){
            this.name=name;
        }
        public void run(){
            for(int i=0;i<=60;i++){
                System.out.println(name+"运行"+i);
            }
        }
    
        public static void main(String[]args){
            HelloThread h1 = new HelloThread("A副线程");//创建一个线程
            //HelloThread h2 = new HelloThread("B");
            //两个线程并发的运行
            h1.start();//开启线程,并执行该线程的run方法
            //h1.run();//仅仅是对象调用方法,是主线程的顺序执行
            for(int i=0;i<60;i++){
                System.out.println("主线程执行中。。。。。。。。。。"+i);
            }
            //h2.start();
        }
    }
    复制代码

    总结:通过代码的测试:说明开启线程是使用start方法,虚拟机会自动调用run方法,等到抢到CUP使用权,线程从就绪状态转为运行状态,而不能直接调用run()方法,因为在main方法里面如果调用run方法,相当于jvm主线程执行了一个普通的方法。所以是不能实现多线程,只是主线程在顺序的往下执行。

    java基础知识回顾之java Thread类--java线程实现常见的两种方式实现Runnable接口(二)

     

    创建线程的第二中方式:

    /**
     *
         步骤:

          1定义类实现Runnable接口
          2.实现Runnable接口中的run方法。
          3.通过Thread类建立线程对象,并将Runnable 接口的子类对象作为实际参数传给Thread类的构造方法、
          4.调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法
          为什么要将Runnable接口子类的对象传递给Thread类的构造方法?
          因为线程的任务(要运行的代码)都封装在Runnable接口的子类对象的run方法中,所以在线程对象创建的时候就必须明确要运行的任务。
     *
     */

    复制代码
    package com.lp.ecjtu.Thread;
    
    public class HelloRunnableThread  implements Runnable{
        private String name;
        
    
        public String getName() {
            return name;
        }
    
    
        public void setName(String name) {
            this.name = name;
        }
        
        public HelloRunnableThread(String name){
            this.name = name;
        }
    
        @Override
        public void run() {
            for(int i=0;i<=100;i++){
                System.out.println(name+"运行"+i);
            }
        }
        public static void main(String[] args){
            HelloRunnableThread h1 = new HelloRunnableThread("A");
            HelloRunnableThread h2 = new HelloRunnableThread("B");
            Thread t1 = new Thread(h1);//h1为传入线程构造方法的接口
            Thread t2 = new Thread(h2);
            System.out.println("线程启动之前---》"+t1.isAlive());//测试线程是否处于活动状态
            t1.start();//开启线程,进入可运行状态
            System.out.println("线程启动之后---》"+t1.isAlive());
            t2.start();
        }
    }
    复制代码

    输出:

    线程启动之前---》false
    线程启动之后---》true
    A运行0
    B运行0
    B运行1
    B运行2
    B运行3
    B运行4
    B运行5
    B运行6
    B运行7
    B运行8
    B运行9
    B运行10
    B运行11
    B运行12
    B运行13
    A运行1
    B运行14
    A运行2
    B运行15

     

    java基础知识回顾之java Thread类学习(三)--java线程实现常见的两种方式实现好处:

     

    总结:实现Runnable接口比继承Thread类更有优势:

    1.因为java只能单继承,实现Runnable接口可以避免单继承的局限性

    2.继承Thread类,多个线程不能处理或者共享同一个资源,但是实现Runnable接口可以处理同一个资源。

    下面我们做个测试:验证下。车站的售票系统售票的例子,车站的各个售票口相当于各个线程,我们先使用第一种方法几继承Thread类的方式实现:

    代码如下:

    复制代码
    package com.lp.ecjtu.Thread;
    /**
     * 
     * @author Administrator
     * 车站的售票系统售票的例子,车站的各个售票口相当于各个线程。
     * 
     */
    public class TicketsThread  extends Thread{
        //用静态变量存放这100张票,这样就不会卖重复
        private  int tickets = 100;
        public void run(){
            while(true){
                if(tickets > 0){
                    System.out.println(Thread.currentThread().getName()+"***sale***"+(--tickets));
                }
            }
        }
        /**
         * @param args
         */
        public static void main(String[] args) {
            // 不合理的买票程序,因为,不同线程都可以卖同一张票,
            //现实生活中不是这样的,窗口1买完第99张票,窗口2不可以卖了。
            TicketsThread t1 = new TicketsThread();
            TicketsThread t2 = new TicketsThread();
            TicketsThread t3 = new TicketsThread();
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    复制代码

    代码的原理图如下:

    总结:不合理的卖票方法,不同线程都可以卖同一张票,现实生活中不是这样的,窗口1卖完99号票,窗口2就不可以再卖这张票了。

    怎么解决呢?使用实现Runnable接口的方法就可以解决,代码如下:

    复制代码
    public class TicketsRunnable implements Runnable {
        private int ticket=100;
        //Object obj = new Object();
        public TicketsRunnable(){
            System.out.println("*****************************");
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            
            while(true){
                //synchronized(obj){  //同步代码块
                    if(ticket > 0){//当线程0被调起的时候,当执行到这条判断语句的时候,线程1被调起抢了CPU资源,线程0进入冻结状态。
                        try {
                            Thread.sleep(100);//中断当前活跃的线程,或者执行的线程
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"正在卖票"+ticket--);
                        //System.out.println(Thread.currentThread().getId());
                        //System.out.println(Thread.currentThread().getName());
                    }
                //}
                
            }
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            TicketsRunnable runna = new TicketsRunnable();
            Thread t1 = new Thread(runna);
            Thread t2 = new Thread(runna);
            Thread t3 = new Thread(runna);
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    复制代码

    上面代码简单内存图理解如下:

    总结:三个窗口再卖100张票,直到卖完,3个线程才停止。卖的票的号码没有重复,真正实现了现实生活中的卖票的效果。但是存在一个线程安全问题,通过测试运行发现,打印出了0,-1,-2等错票,多线程运行出现了安全问题,这就需要引入线程锁机制,下一篇博客详细的讲解怎样避免这一问题。

     
  • 相关阅读:
    JS来推断文本框内容改变事件
    LINQ To SQL 语法及实例大全
    linux-多线程
    BackTrack5 (BT5)无线password破解教程之WPA/WPA2-PSK型无线password破解
    用Jfree实现条形柱状图表,java代码实现
    OpenGL中glPushMatrix和glPopMatrix的原理
    C# 之 抽象类与接口
    Android漫游记(1)---内存映射镜像(memory maps)
    Web页面布局方式小结
    STL中主要的算法(一)
  • 原文地址:https://www.cnblogs.com/hanease/p/15721245.html
Copyright © 2011-2022 走看看