加载中...

Java多线程(一)线程的创建

Java多线程

1.进程

对于一个CPU而言,在某个时间点只能运行一个程序,也就是只能执行一个进程,操作系统会为每个进程分配一段有限的CPU使用时间,CPU在这段时间执行某个进程,下一段时间切换到下一个进程,由于CPU的运行速度非常快,能在极短时间内在不同进程间切换,所以给人同时执行多个程序的感觉。

2.线程

在多任务操作系统中,每个运行的程序都是一个进程,而在一个进程中可以有多个执行单元同时运行,来完成一个或多个程序任务,这些执行单元可以看作程序执行的一条条线索,被称为线程。操作系统中每个进程至少存在一个线程。当一个Java程序启动时,就会产生一个进程,该进程中会默认创建一个线程,在这个线程上会运行main()方法中的代码。如果希望程序中的多段代码交替运行,需要创建多个线程,即多线程程序,每个线程之间是独立的,可以并发执行。多线程看似是并发执行,其实和进程一样由CPU控制轮流执行,由于CPU的运行速度非常快,所以给人同时执行的感觉。

3.线程的创建

线程创建的三种方法:

  • 继承Thread类,重写run()方法
  • 实现Runnable接口,重写run()方法
  • 实现Callable接口,重写call()方法,并使用Future来获取call()方法的返回结果

(1)Thread

步骤:

(1)创建一个Thread类的子类(子线程),同时重写Thread类的run()方法
(2)创建该子类的实例对象,并通过调用start()方法启动线程

举例:

class MyThread1 extends Thread{
    public MyThread1(String name){
        super(name);
    }
    public void run(){
        int i=0;
        while(i++<5){
            System.out.println(Thread.currentThread().getName()+"的run()方法在运行");
        }
    }
}

public class Page352 {
    public static void main(String[] args) {
        MyThread1 thread1=new MyThread1("thread1");
        thread1.start();
        MyThread1 thread2=new MyThread1("thread2");
        thread2.start();
    }
}

提示:currentThread()是Thread类的静态方法,用来获取当前线程对象,getName()方法用来获取线程名称

(2)Runnable

通过继承Thread类实现多线程有一定的局限性,因为Java只支持类的单继承,这时就可以考虑通过实现Runnable接口来实现多线程

步骤:

(1)创建一个Runnable接口的实现类,同时重写run()方法
(2)创建Runnable接口的实现类对象
(3)使用Thread有参构造方法创建线程实例,并将Runnable接口的实现类的实例对象作为参数传入
(4)调用线程实例的start()方法启动线程

举例:

class MyThread2 implements Runnable{
    public void run(){
        int i=0;
        while(i++<5){
            System.out.println(Thread.currentThread().getName()+"的run方法正在运行");
        }
    }
}


public class Paeg353 {
    public static void main(String[] args) {
        MyThread2 myThread2=new MyThread2();
        Thread thread1=new Thread(myThread2,"thread1");
        thread1.start();
        Thread thread2=new Thread(myThread2,"thread2");
        thread2.start();
    }
}

先创建Runnable接口的实现类对象myThread2,然后将myThread2作为Thread构造方法的参数创建线程实例

(3)Callable

前两种方法实现多线程时需要重写run()方法,但该方法没有返回值,因此无法从多个线程中获取返回结果,这时就可以使用Callable接口。Callable与Runnable实现多线程一样,都是通过Thread类的有参构造方法传入Runnable接口类型的参数来实现多线程,不同的是这里传入的是Runnable接口的子类FutureTask对象作为参数,而FutureTask对象中则封装带有返回值的Callable接口实现类。

步骤:

(1)创建一个Callable接口的实现类,同时重写Callable接口的call()方法
(2)创建Callable接口的实现类对象
(3)通过FutureTask线程结果处理类的有参构造方法来封装Callable接口实现类对象
(4)使用参数为FutureTask类对象的Thread有参构造方法创建Thread线程实例
(5)调用线程实例的start()方法启动线程

举例:

import java.util.concurrent.*;

class MyThread3 implements Callable<Object>{
    public Object call() throws Exception{
        int i=0;
        while(i++<5){
            System.out.println(Thread.currentThread().getName()+"的run方法在运行");
        }
        return i;
    }
}

public class Page355 {
    public static void main(String[] args) throws InterruptedException,ExecutionException{
        MyThread3 myThread3=new MyThread3();
        FutureTask<Object>ft1=new FutureTask<>(myThread3);
        Thread thread1=new Thread(ft1,"thread1");
        thread1.start();
        FutureTask<Object>ft2=new FutureTask<>(myThread3);
        Thread thread2=new Thread(ft2,"thread2");
        thread2.start();
        System.out.println(ft1.get()+"    "+ft2.get());
    }
}

4.实现多线程方式的对比

背景:四个窗口出售100张车票

Thread实现:

class TicketWindow extends Thread{
    private int tickets=100;
    public void run(){
        while(true){
            if(tickets>0){
                System.out.println(Thread.currentThread().getName()+"正在发售第"+tickets--+"张票");
            }
        }
    }
}

public class Page357 {
    public static void main(String[] args) {
        new TicketWindow().start();
        new TicketWindow().start();
        new TicketWindow().start();
        new TicketWindow().start();
    }
}

上面的代码是错误的,每张票都被发了四次,因为四个线程没有共享100张票,四个TicketWindow对象各有一个tickets变量。

如果没有通过构造方法指定线程名称,系统会默认生成线程名称。

Runnable实现:

class TicketWindow2 implements Runnable{
    private int tickets=100;
    public void run(){
        while(true){
            if(tickets>0){
                System.out.println(Thread.currentThread().getName()+"正在发售第"+tickets--+"张票");
            }
        }
    }
}

public class Page358 {
    public static void main(String[] args) {
        TicketWindow2 tw=new TicketWindow2();
        new Thread(tw,"窗口1").start();
        new Thread(tw,"窗口2").start();
        new Thread(tw,"窗口3").start();
        new Thread(tw,"窗口4").start();
    }
}

这个代码是正确的,通过Runnable接口的方式只创建了一个Ticket Window2对象,然后创建了四个线程,每个线程都同一个TicketWindow2对象中的run方法,这样四个线程访问的就是同一个tickets变量

posted @ 2022-03-03 17:38  我没有bug  阅读(56)  评论(0编辑  收藏  举报