多线程(一)线程实现方式

1.并发与并行

并发:两个或多个事件在同一时间段发生(交替执行)

并行:两个或多个事件在同一时刻发生(同时发生)

            

2.进程与线程

3.线程调度

分时调度:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间

抢占式调度:优先让优先级高的线程使用cpu,如果线程的优先级相同,那么随机选择一个(线程随机性),java使用的是抢占式调度

4.主线程

主线程:执行主方法的线程

单线程程序:java程序中只有一个线程

5.创建多线程程序

(1)第一种方式:创建Thread类的子类

     步骤:

                 创建一个Thread类的子类

                 在Thread类的子类中重写Thread类的run 方法,设置线程任务(开启线程要做什么)

                 创建Thread类的子类对象

                 调用Thread类中的start方法,开启新的线程,执行run方法

                          start方法使该线程进入就绪状态;java虚拟机调用该线程的run方法执行

                          结果是两个线程(主线程与创建的新线程)并发执行

                          多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动

package com.goldencis.thread;
/**
 * @author
* */
public class Thread1 extends Thread{
    private String name;
    private static int RUN_NUM = 5;

    public Thread1(String name) {
        this.name = name;
    }

    @Override
    public void run(){
      for(int i = 0; i <= RUN_NUM; i++){
          System.out.println(name + "运行" + i);
          try {
              sleep((int)Math.random()*10);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
    }

    public static void main(String[] args) {
        Thread1 thread1 = new Thread1("张三");
        Thread1 thread11 = new Thread1("里斯");
        //Thread1 thread11 = thread1;
        thread1.start();
        thread11.start();
        for(int i = 0; i <= RUN_NUM; i++){
            System.out.println("主线程" + "运行" + i);
            try {
                sleep((int)Math.random()*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(2) 第二种方式:实现Runable接口

  • public interface Runnable

    Runnable接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run

  • public Thread()

       分配一个新的Thread对象。 此构造具有相同的效果Thread (null, null, gname) ,其中gname是新生成的名字。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。

       分配一个新的Thread对象。 该构造函数具有与Thread (null, target, gname)相同的效果,其中gname是新生成的名称。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。

步骤:
        定义Runnable接口的实现类,并重写该接口的run()方法,设置线程任务(开启线程要做什么)

        创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象

        调用线程对象的start()方法来启动线程

package com.goldencis.thread;

public class Thread2 implements Runnable {
    private String name;
    private static int RUN_NUM = 5;

    public Thread2(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for(int i = 0; i <= RUN_NUM; i++){
            System.out.println(name + "运行" + i);
            try {
                Thread.sleep((int)Math.random()*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread2 t = new Thread2("张三");
        Thread thread = new Thread(t);
        thread.start();
    }
}

实现Runnable接口创建多线程程序的优点

(3)第三种方式:通过 Callable 和 Future 创建线程

   步骤:

        创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

        创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

        使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

        调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

(4)匿名内部类方式实现线程创建

匿名:没有名字

内部类:写在其他类内部的类

匿名内部类的作用:简化代码

         把子类继承父类,重写父类方法,创建子类对象合一步完成

         把实现类实现类接口,重写接口中方法,创建实现类对象合一步完成

匿名内部类的最终产物:子类/实现类对象,而这个对象没有名字

格式:
   new 父类/接口(){

        重写父类/接口中的方法

  };

 

package com.goldencis.thread;

public class InnerThread {
    public static void main(String[] args) {
        //线程的父类是Thread
        //new MyThread().start()
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "黑马");
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        //线程的接口是Runnable
        //Runnable r = new RunnableImpl()
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "白马");
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        new Thread(r).start();

        //简化接口的方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "都是好马");
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        ).start();
    }
}

6.线程原理

随机打印结果

多线程内存图解

7.Thread类常用方法

获取线程名称:

  • 使用Thread类中的getName()方法

          String getName()  返回该线程的名称

  • 可以先获取当前正在执行的线程,使用线程中getName()获取线程名称

           static Thread currentThread()  返回当前正在执行的线程对象的引用,是 Thread类中的静态方法

设置线程名称

  • 使用Thread类中的setName()方法

    void setName(String name)  将此线程的名称更改为等于参数 name

  • 创建一个带参构造方法,参数传递线程的名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子线程起个名字

    Thread(String name)  分配新的Thread对象

          

sleep

public static void sleep(long millis) throws InterruptedException

使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权。

参数

millis - 以毫秒为单位的睡眠时间长度

异常

IllegalArgumentException - 如果 millis值为负数

InterruptedException - 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。

 

public static void sleep(long millis,int nanos)throws InterruptedException

导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。 线程不会丢失任何显示器的所有权

8. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值