002-多线程的创建

多线程程序的引入:

 

 

 

 

 


 


 

 

如何实现多线程程序呢?


         由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
         而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
         Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
         但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
         由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
         然后提供一些类供我们使用。我们就可以实现多线程程序了。

 


 

方式1:继承Thread类
 

步骤
         A:自定义类MyThread继承Thread类。
         B:MyThread类里面重写run()?     

        C:创建对象
        D:启动线程

 

几个问题:


 

为什么要重写run()方法?

           因为不是类中的所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。

 


 

run()和start()的区别?

       run():仅仅是封装被线程执行的代码,直接调用是普通方法

       调用run()方法是单线程的效果,
       是因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果。

 

      start():首先启动了线程,然后再由 jvm去调用该线程的run()方法。产生的是多线程的效果。

 


 

为什么一个线程对象调用2次或2次异常会产生IllegalThreadStateException(非法的线程状态异常)?

 

MyThread t1 = new MyThread();  // 创建线程对象 t1

t1.start();   
t1.start();

 

结果显示:会产生IllegalThreadStateException(非法的线程状态异常)

 

          因为这个相当于是my线程被调用了两次。而不是两个线程启动。

 


  

代码:

MyThread.java

 

public class MyThread extends Thread {

    @Override
    public void run() {

    
        for(int i = 1; i <= 200; i++) {
            System.out.println("MyThread-----"+i);
            
        }
    }

}

 

 

 

 

Test.java

 

public class MyThreadDemo {
    public static void main(String[] args) {
        // 创建线程对象
        // MyThread my = new MyThread();
        // // 启动线程
        // my.run();
        // my.run();
        // 调用run()方法为什么是单线程的呢?
        // 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
        // 要想看到多线程的效果,就必须说说另一个方法:start()
        // 面试题:run()和start()的区别?
        // run():仅仅是封装被线程执行的代码,直接调用是普通方法
        // start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
        // MyThread my = new MyThread();
        // my.start();
        // // IllegalThreadStateException:非法的线程状态异常
        // // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
        // my.start();

        // 创建两个线程对象
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

        my1.start();
        my2.start();
    }
}

 

 

 


线程的基本操作:

1. 如何获取线程对象的名称呢?
     public final String getName():获取线程的名称。
 

2.  如何设置线程对象的名称呢?
     public final void setName(String name):设置线程的名称
 


 3. 针对不是Thread类的子类中如何获取线程对象名称呢?

    public static Thread currentThread():   返回当前正在执行的线程对象
     Thread.currentThread().getName()

 

public class MyThread extends Thread {
    
    // 无参构造
    public MyThread() {

    }
    
    // 有参构造
     public MyThread(String name) {
         
     //super(name);  也可写成这种形式
     setName(name);  // 修改线程的名字
        
    }
    
    

    @Override
    public void run() {

    
        for(int i = 1; i <= 200; i++) {
            System.out.println(getName()+"---------"+i);   // 获取线程名 + 打印i的值
            
        }
    }

}

 

 

 

 

测试类

 

public class Test {

    public static void main(String[] args) {

        //1.无参构造的方式,构造线程对象
        
        MyThread t1 = new MyThread();  // 创建线程对象 t1
        MyThread t2 = new MyThread();  // 创建线程对象 t2

        // 获取t1线程对象的名字
        System.out.println(    "t1默认线程名:"+t1.getName());   // Thread-0
        
       //  获取t2线程对象的名字
        System.out.println("t2默认线程名:"+    t2.getName());   // Thread-1
        
        // 修改线程t1的名称
        t1.setName("实况足球");
        
        // 修改线程t2的名称
        t2.setName("侠盗飞车");
        
 
        t1.start();   // 启动线程t1
        t2.start();   // 启动线程t2
        System.out.println();
        
        
        // 2.有参构造的方式构造线程对象的同时,修改线程名称
        MyThread t3 = new MyThread("阿里巴巴");
        MyThread t4 = new MyThread("央视影音");
        
        t3.start();  // 启动线程t3
        t4.start();  // 启动线程t4
        
        
        
        // 3.获取main方法所在的线程对象的名称
        Thread thread = Thread.currentThread();  // 返回当前正在执行的线程对象
        System.out.println(    "main方法所在的线程对象的名称: "+thread.getName());   // 打印线程名称
        
                
        
    }

}

 

 

 

分析打印结果:

 

 

产生问题:   默认线程名称格式为什么是:Thread-? 编号    如:Thread-0       Thread-1

查看Thread类的源码:

 

class Thread {
    private char name[];

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    
     private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        //大部分代码被省略了
        this.name = name.toCharArray();
    }
    
    public final void setName(String name) {
        this.name = name.toCharArray();
    }
    
    
    private static int threadInitNumber; //0,1,2
    private static synchronized int nextThreadNum() {
        return threadInitNumber++; //return 0,1
    }
    
    public final String getName() {
        return String.valueOf(name);
    }
}

class MyThread extends Thread {
    public MyThread() {
        super();
    }
}

 

 

 

 

 

 


 

 

public final void setDaemon(boolean on):    将该线程标记为守护线程或用户线程。


 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。

 游戏:坦克大战。               

 

 

public class ThreadDaemon extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}

 

 

 

 

 

public class ThreadDaemonTest {
    public static void main(String[] args) {
        
        ThreadDaemon td1 = new ThreadDaemon();
        ThreadDaemon td2 = new ThreadDaemon();

        td1.setName("关羽");
        td2.setName("张飞");

         
        td1.setDaemon(true);  // 将td1线程标记为守护线程
        td2.setDaemon(true);  // 将td2线程标记为守护线程

        td1.start();    // 启动线程td1
        td2.start();    // 启动线程td2

        Thread.currentThread().setName("刘备"); // 设置当前主线程名字为"刘备"
        
        for (int x = 0; x < 5; x++) {
            
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}

 

 

 

分析打印结果

 

 



  public final void join()  : 等待该 线程终止

 

public class ThreadJoin extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}

 

 

 

 

package com.itcast_04;

public class ThreadJoinTest {
     
        public static void main(String[] args) {
            // 创建3个线程
            ThreadJoin tj1 = new ThreadJoin();
            ThreadJoin tj2 = new ThreadJoin();
            ThreadJoin tj3 = new ThreadJoin();

            tj1.setName("朱元璋");
            tj2.setName("朱允炆");
            tj3.setName("朱  棣");

            tj1.start();   // 开启线程tj1
            
            try {
                tj1.join();  // 等待tj1线程终止
                
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            tj2.start();   // 开启线程tj2 
            tj3.start();  //  开启线程tj3
        }
    }
 
 

 

 

分析结果:

 

 

 



public final int   getPriority():返回线程对象的优先级

public final void  setPriority(int newPriority):更改线程的优先级。

 
 
 注意:
        线程默认优先级是5。
        线程优先级的范围是:1-10。
        线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
        
 IllegalArgumentException:   非法参数异常。
           -抛出的异常表明向方法传递了一个不合法或不正确的参数。
 

 

public class ThreadPriority extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }

}

 

 

public class ThreadPriorityTest {

    public static void main(String[] args) {
        
        // 创建3个线程对象
        ThreadPriority tp1 = new ThreadPriority();
        ThreadPriority tp2 = new ThreadPriority();
        ThreadPriority tp3 = new ThreadPriority();

        // 更改线程名称
        tp1.setName("乔 峰");
        tp2.setName("虚 竹");
        tp3.setName("段 誉");

        // 获取默认优先级
          System.out.println("打印tp1默认优先级:"+tp1.getPriority());
          System.out.println("打印tp2默认优先级:"+tp2.getPriority());
          System.out.println("打印tp3默认优先级:"+tp3.getPriority());
          System.out.println();

        // 设置线程优先级
        // tp1.setPriority(100000);  // 报错原因:超出1-10这个范围
        
        //设置正确的线程优先级   [1-10]
        tp1.setPriority(10);  // 最大值
        tp2.setPriority(1);   // 最小值

        tp1.start();
        tp2.start();
        tp3.start();
         

    }

}

 

 

 


 

public static void sleep(long millis)       线程休眠
   

 

import java.util.Date;

public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x + ",日期:" + new Date());
             
            try {
                Thread.sleep(2000);   // 休眠2秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

 

 

public class ThreadSleepTest{
    public static void main(String[] args) {
        // 创建三个线程对象
        ThreadSleep ts1 = new ThreadSleep();
        ThreadSleep ts2 = new ThreadSleep();
        ThreadSleep ts3 = new ThreadSleep();

        // 设置线程名称
        ts1.setName("许一乐");
        ts2.setName("许二和");
        ts3.setName("许三多");

        // 开启3个线程
        ts1.start();
        ts2.start();
        ts3.start();
    }
}

 

 


 



  public final void stop( )   :让线程停止, 过时了,但是还可以使用。
  public void interrupt( )    : 中断线程。    把线程的状态终止,并抛出一个InterruptedException。

 

 

package com.itcast_04;
    import java.util.Date;

    public class ThreadStop extends Thread {
        @Override
        public void run() {
            System.out.println("开始执行:" + new Date());

            // 我要休息10秒钟,亲,不要打扰我哦
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // e.printStackTrace();
                System.out.println("线程被终止了");
            }

            System.out.println("结束执行:" + new Date());
        }
    }

 

 

 

 

public class ThreadStopTest {

    public static void main(String[] args) {
        
        // 创建线程对象
        ThreadStop ts = new ThreadStop();
        
        // 开启线程
        ts.start();

        try {
            
            // 你超过三秒不醒过来,我就干死你
            Thread.sleep(3000);  // 主线程休眠3秒
            // ts.stop();  // 过时方法  缺点:过于暴力
            
            ts.interrupt();
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

 


   

 


 

 


  public static void yield( )   :暂停当前正在执行的线程对象,并执行其他线程。 (礼让)
  让多个线程的执行更和谐,但是不能靠它保证一人一次。

 

 

public class ThreadYield extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
            Thread.yield();  // 线程的礼让
        }
    }

}

 

 

 

 

public class ThreadYieldTest  {

    public static void main(String[] args) {
        
        ThreadYield ty1 = new ThreadYield();
        ThreadYield ty2 = new ThreadYield();

        ty1.setName("詹姆斯");
        ty2.setName("小 毛");

        ty1.start();
        ty2.start();
    }

}

 

 

 

 

 

分析结果:

      有缺陷,可以使用 唤醒机制来解决

 

 


 方式2:实现Runnable接口
  步骤:
          A:自定义类MyRunnable实现Runnable接口
          B:重写run()方法
          C:创建MyRunnable类的对象
          D:创建Thread类的对象,并把C步骤的对象作为构造参数传递

 


 

 

 

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            
            // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }

    }

}

 

 

 

 

 

public class MyRunnableTest {

    public static void main(String[] args) {

        // 创建MyRunnable类的对象 (Runnable接口的实现)
        MyRunnable my = new MyRunnable();

        // 创建Thread类的对象,并把C步骤的对象作为构造参数传递
        // 构造方式一:Thread(Runnable target)
        // Thread t1 = new Thread(my);
        // Thread t2 = new Thread(my);
        // t1.setName("詹姆斯");
        // t2.setName("小  毛");

        // 构造方式二: Thread(Runnable target, String name)
        // 创建两个线程,并修改线程名字
        Thread t1 = new Thread(my, "林青霞");
        Thread t2 = new Thread(my, "刘意");
        
        // 开启线程
        t1.start();
        t2.start();

    }

}

 

 

 

 

 

 

 


 

 2种创建多线程方式的比较:

 

 

 


 

 

   
 

 

 

 

 

 

 


 

 

 

posted @ 2019-11-09 21:39  小茅棚  阅读(205)  评论(0编辑  收藏  举报