线程的创建和使用

本文详细介绍了Java中实现多线程的四种方式:继承Thread类、实现Runnable接口、实现Callable接口及使用线程池,并通过实例代码展示了线程通信的重要性,包括wait()、notify()和notifyAll()的使用。同时,文章提到了线程池的优势以及ExecutorService的execute()方法。最后,通过生产者/消费者问题展示了线程通信在实际应用中的场景。
摘要由CSDN通过智能技术生成

方式一:继承于Thread类


* 1.创建一个继承于Thread类的子类
* 2.重写Thread类的run()方法(将此线程的操作声明在方法中)
* 3.创建Thread类的子类的对象
* 4.通过此对象调用start()方法

//例子:遍历100以内所有的偶数

//1.创建一个继承于Thread类的子类
class myThread extends Thread {
    //2.重写Thread类的run()方法
    @Override
    public void run() {
    //重写Thread类的run()方法
        for (int i = 0;i<=100;i++){
            if(i % 2 == 0){
                System.out.println(i);
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
    //3创建Thread类的子类的对象
        myThread t1 = new myThread();
    //4.通过此对象调用start()方法;①启动该当前线程,②调用当前线程的run()方法
        t1.start();
        System.out.println("hello");
        //问题一:我们不能通过直接调用run()的方法启动线程
        //t1.run();
        //问题二:再启动一个线程,遍历100以内的偶数,不可以还让已经start的线程去执行
        //会报:IllegalThreadStateException
        //我们需要重新创建一个线程对象,并调用start方法
        t1.start();


    //如下的操作仍然是在main线程中执行的
        for (int i = 0;i<=100;i++){
            if(i % 2 == 0){
                System.out.println(i+"****main*****");
            }
        }

    }
}

方式二:创建实现Runnable接口的类


* 1.创建一个实现Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run();
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start();

//1.创建一个实现Runnable接口的类
class Mthread implements Runnable {
//2.实现类去实现Runnable中的抽象方法:run();
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        Mthread mthread = new Mthread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(mthread);//原构造器形参是Runable接口对象,这里使用的接口实现类的对象,构成多态
        //5.通过Thread类的对象调用start();①启动线程②调用当前线程的run()-->调用了Runable类型的target的run(),
        // target通过构造器被赋值为mthread
        t1.setName("线程1");
        t1.start();
        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(mthread);
        t2.setName("线程2");
        t2.start();
    }

方式三:实现Callable接口 -->JDK 5.0新增

面试题:如何理解实现Callable接口比实现run接口创建多线程强大?

1.call()方法有返回值 ;

2.call()方法抛出异常 ;

3.callable接口是支持泛型的;

接口:                      interface Callable{}   -----------------interface  Runnable{}

实现类:     class NewThread{}   ————>  class FutherTest{}  ————> class Thread{}.start()

//1.创建一个实现callable的实现类
class NumberThread implements Callable{
    //实现call()方法的重写,将此线程执行的操作声明在call()
    @Override
    public Object call() throws Exception {//回调方法
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i%2==0){
                System.out.println(i);
                sum+= i;
            }
        }
        return sum;//也可以return null
    }
}
public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumberThread numberThread = new NumberThread();
        //4.将此Callable实现类的对象作为参数传递到FutureTask构造器的,创建FutureTask对象
        FutureTask futureTask = new FutureTask(numberThread);
        //将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象并调用start方法
        new Thread(futureTask).start();//futureTask也实现了runable接口
        try {
            //6.获取Callable中call方法的返回值
            //get()的返回值就是FutureTask构造器参数Callable实现类重写的call()方法的返回值
            Object sum = futureTask.get();//get的目的是获取call方法的返回值,可以不调
            System.out.println("总和为:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

方式四:使用线程池

好处:
* 1.提高响应速度(减少了创建线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都新创建)
* 3.便于线程管理(调用下列方法)

*      corePoolSize:核心池的大小
*      maximumpoolSize:最大线程数
*      keepAliveTime:线程没有任务时最多保持多长时间会终止

*      面试题:创建线程有几种方式?四种
*      1、继承Thread()类
*      2、实现Run()接口
*      3、实现Callable接口 -->JDK 5.0新增
*      4、使用线程池

execute:方法

Executors:创建线程池的工具类

class NumberThread1 implements Runnable{//实现线程的功能
    @Override
    public void run() {
        for (int i = 0; i <= 100 ; i++) {
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName() +":"+ i);
            }
        }
    }
}
class NumberThread2 implements Runnable{//实现线程的功能
    @Override
    public void run() {
        for (int i = 0; i <= 100 ; i++) {
            if (i % 2 != 0){
                System.out.println(Thread.currentThread().getName() +":"+ i);
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //1.提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);//service是ExecutorService接口的引用,是常量
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;//向下强转成实现类的类型的对象
        //设置线程池的属性
        service1.setCorePoolSize(10);
        service1.setKeepAliveTime(10);

        //2.执行指定的线程操作,需要提供实现Runna接口或者Callable接口实现类的对象
        NumberThread1 numberThread1 = new NumberThread1();
        NumberThread2 numberThread2 = new NumberThread2();

        service.execute(numberThread1);//适合使用于Runnable
        service.execute(numberThread2);
        service.submit(Callable callable);//适合是用于Callable
        //3.关闭连接池
        service.shutdown();
    }

}

线程的生命周期

 

线程的通信 

线程通信的例子,使用两个线程打印1-100,线程1 线程2交替打印

* wait();一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
* notify();一旦执行此方法,就会唤醒wait的一个线程,如果多个线程wait状态,则唤醒优先级高的那个;
* notifyAll();一旦执行此方法,唤醒所有被wait的线程;

* 说明:
* 1.wait(),notify(),notifyAll()三个方法必须使用在synchronized同步代码块或者同步方法中.
* 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。 否则会出现IllegalMonitorStateException异常
* 3.wait(),notify(),notifyAll()三个方法均为定义在java.lang.Object类中(方便任意一个对象当同步监视器时可以调用这个方法)。
*
* 面试题:sleep()和wait()方法的异同
 相同点:一旦执行方法都可以使得当前线程进入阻塞状态
不同点:1)两个方法声明的位置不同:Thread类中声明的sleep(),Objet类中声明的wait();
*             2)调用的范围不同:sleep()可以在任何需要的场景调用Thread.sleep(100)静态方法,
*                wait()必须同步监视器才能调用,即必须使用在同步方法或者同步代码块中。
*             3)关于是否释放同步监视器;如果两个方法都使用在同步代码块或者同步方法中,sleep()
*                不会释放同步监视器,wait()会释放。

class Number implements Runnable {
    private int numeber = 1;
    private Object obj = new Object();

    @Override
    public void run() {

        while (true) {
            synchronized (obj) {//Number的对象充当同步监视器
                obj.notify();//唤醒阻塞的 线程
                if (numeber <= 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + numeber);
                    numeber++;
                    try {
                        obj.wait();//执行wait会释放锁,使得线程2得以拿到锁进入
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);
        t1.setName("甲");
        t2.setName("乙");
        t1.start();
        t2.start();

    }
}

线程通信的应用:生产者/消费者问题



       生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产:如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
这里可能出现两个问题:
>生产者比消费者快时,消费者会漏掉一些数据没有取到。
>消费者比生产者快时,消费者会取相同的数据。

分析
1.是否多线程问题?是,生产者线程、消费者线程
2.是否有共享数据?是,店员、产品数量
3.如何来解决线程安全问题?同步机制,有三种方法
4.是否涉及到线程的通信?是

class Clerk{//店员
    private int productNumber = 0;
    //生产产品
    public synchronized void produceProduct() {
        if (productNumber<20){
            productNumber++;
            System.out.println(Thread.currentThread().getName()+":开始生产第"+productNumber+"个产品");
            notify();//唤醒消费者 来消费
        }else{
            try {
                wait();//等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //消费产品
    public synchronized void consumeProduct() {
        if (productNumber>0){
            System.out.println(Thread.currentThread().getName() +"开始消费第"+productNumber + "个产品");
            productNumber--;
            notify();//唤醒生产者来生产
        }else{
            try {
                wait();//等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Producer extends Thread{//生产者
    private Clerk clerk;

    @Override
    public void run() {
        System.out.println(getName()+ ":"+"开始生产产品");
        while (true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
}
class Customer extends Thread{//消费者
    private Clerk clerk;

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName()+ ":"+"开始消费产品");
        while (true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}

public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();//唯一的 Clerk对象,作为同步监视器
        Producer p1 = new Producer(clerk);
        p1.setName("生产者1");
        Customer c1 = new Customer(clerk);
        c1.setName("消费者1");
        Customer c2 = new Customer(clerk);
        c2.setName("消费者2");
        p1.start();
        c1.start();
        c2.start();

    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值