Java二阶-37-线程与进程

1. 什么是线程和进程?

进程(Process):是系统进行资源分配和调度的一个独立单元。它是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统动态执行的基本单元。每个进程都有自己独立的地址空间,包含文本区域、数据区域、堆栈等,并且进程之间不共享内存。

线程(Thread):是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能够独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、寄存器集合和堆栈),但它与同属一个进程的其他线程共享父进程的地址空间。因此,线程之间可以直接读写进程数据段(如全局变量)来进行通信,但也需要一定的同步机制。

线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程中可以并发多个线程,每个线程有自己的程序计数器、寄存器集合和堆栈,用于记录线程运行的位置和状态。线程是独立运行的,它独立于创建它的进程中的其他线程。

2. 为什么使用线程?

使用线程的主要原因包括:

  1. 提高程序执行效率:多线程程序可以并行处理任务,从而提高程序的执行效率。特别是在多核CPU的计算机上,多线程程序可以充分利用硬件资源,加速程序的执行。

  2. 改善用户体验:在图形用户界面(GUI)程序中,主线程通常用于处理用户的输入和界面的更新,而耗时操作(如文件读写、网络请求等)可以放在其他线程中执行,避免界面冻结,提高用户体验。

  3. 简化编程模型:线程提供了一种简化的并发编程模型,使得开发者可以更容易地编写出能够处理多个任务同时进行的程序。

  4. 有效利用资源:通过线程,程序可以更有效地利用系统资源,如CPU、内存等,尤其是在执行大量I/O操作时。

2. 创建线程的方式?

在Java中,创建线程主要有以下几种方式:

  1. 继承Thread类:通过继承java.lang.Thread类并重写其run()方法,可以创建一个新的线程。然后,通过创建该类的实例并调用其实例的start()方法来启动线程。start()方法会调用run()方法,但run()方法不会直接启动线程,而是通过start()方法间接启动。                

    //创建第一个线程
    public class MyThread extends Thread{
        //run表示线程启动后执行的业务代码
        @Override
        public void run() {
            System.out.println("线程执行中");
            for (int i =0;i<100;i++){
                //getNmae()获取线程名称:默认名称:Thread-n,n表示线程编号 ,该方法必须在Thread类下才能用的方法
                System.out.println(Thread.currentThread().getName()+"i="+i);
            }
        }
    }
    public class Test01 {
        public static void main(String[] args) {
            //创建线程对象
            MyThread myThread = new MyThread();
            //启动线程--硬件底层调用线程的run方法
            myThread.start();
            for (int i = 0; i < 10; i++) {
                System.out.println("main线程:" + i);
            }
        }
    }
    public class SellTicket extends Thread{
        private static int ticket = 100;
        @Override
        public void run()
        {
            while (true)
            {
                if (ticket > 0)
                {
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
                    ticket--;
                }
                else
                {
                    break;
                }
            }
        }
    }
    public class Test02 {
        public static void main(String[] args)
        {
            SellTicket st = new SellTicket();
            st.start();
            SellTicket st02 = new SellTicket();
            st02.start();
            SellTicket st03 = new SellTicket();
            st03.start();
            SellTicket st04 = new SellTicket();
            st04.start();
    
        }
    }
  2. 实现Runnable接口:通过实现java.lang.Runnable接口并重写其run()方法,然后将该实现类的实例传递给Thread类的构造器,最后创建Thread类的实例并调用其start()方法来启动线程。这种方式相较于继承Thread类更为灵活,因为Java不支持多重继承,但可以实现多个接口。实现Runnable接口的方式更加灵活,因为Java不支持多重继承,但可以实现多个接口,这使得一个类可以同时继承其他类并实现Runnable接口。此外,Runnable接口还可以被用于ExecutorService等高级线程管理工具中。

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            //线程执行的代码
            for (int i = 0; i < 100; i++){
                System.out.println(Thread.currentThread().getName()+" "+i);
            }
        }
    }
    
    public class Test03 {
        public static void main(String[] args) {
            //创建线程任务对象
            MyRunnable mr = new MyRunnable();
            //创建线程对象
            Thread t1 = new Thread(mr, "线程1");
            Thread t2 = new Thread(mr, "线程2");
            t1.start();
            t2.start();
            for (int i = 0; i < 100; i++){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
            System.out.println("main线程结束");
        }
    }
    public class SellRunnable implements Runnable{
        private int ticket = 100;
        @Override
        public void run() {
            while (true){
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票");
                } else {
                    break;
                }
            }
        }
    }
    public class Test04 {
        public static void main(String[] args)
        {
            SellTicket sellTicket04 = new SellTicket();
            Thread thread1 = new Thread(sellTicket04);
            Thread thread2 = new Thread(sellTicket04);
            Thread thread3 = new Thread(sellTicket04);
            Thread thread4 = new Thread(sellTicket04);
            thread1.setName("窗口1");
            thread2.setName("窗口2");
            thread3.setName("窗口3");
            thread4.setName("窗口4");
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
        }
    }
  3. 使用FutureTask与Thread结合FutureTask是一个实现了RunnableFuture接口的类,它允许你为异步计算提供一个结果。你可以通过实现Callable接口(类似于Runnable,但有返回值并且可以抛出异常)来创建一个任务,然后使用FutureTask来包装这个任务,并将FutureTask实例传递给Thread的构造器来启动线程。这种方式适合于需要返回值的异步任务。

    public class MyCallable implements Callable<Double> {
        //call表示线程的任务代码
        @Override
        public Double call() throws Exception {
            double sum = 0;
            for (int i = 0; i < 1000; i+=3) {
                sum += i;
            }
            return sum;
        }
    }
    public class Test05 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyCallable myCallable = new MyCallable();
            //把call对象封装成FutureTask类中,而且任务执行的结果是封装在futureTask中
            FutureTask task = new FutureTask(myCallable);
            //把FutureTask对象封装成Thread类中
            Thread thread = new Thread(task);
            thread.start();
            //获取callable中call方法返回的结果
            Object o = task.get();
            System.out.println(o);
        }
    }
  4. 使用线程池:如ExecutorService等,这种方式可以更加高效地管理线程的生命周期和资源利用。

3. Thread类中常用的方法?

Thread类中常用的方法包括:

  1. start():启动当前线程,导致此线程开始执行;Java虚拟机调用该线程的run方法。

  2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。

  3. currentThread():静态方法,返回对当前正在执行的线程对象的引用。

  4. join():在线程A中调用线程B的join()方法,此时线程A进入阻塞状态,直到线程B执行完之后,线程A结束阻塞状态。

  5. sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),但不会释放任何监视器。

  6. isAlive():测试线程是否处于活动状态。

  7. setName(String name) 和 getName():分别用于设置和获取当前线程的名称。

  8. yield():释放当前线程的当前CPU执行权,让出CPU给其他线程。

  9. setPriority(int newPriority) 和 getPriority():分别用于设置和获取线程的优先级。

4. Runnable和Callable的区别?

Runnable 和 Callable 都是Java中用于多线程编程的接口,但它们之间存在一些关键区别:

  1. 返回值
    • Runnable 接口的 run() 方法没有返回值(即返回类型为 void)。
    • Callable 接口的 call() 方法可以返回一个值,并且返回值的类型可以通过泛型进行指定。
  2. 异常处理
    • Runnable 的 run() 方法不能抛出已检查的异常(checked exceptions),但可以抛出运行时异常(unchecked exceptions)。
    • Callable 的 call() 方法可以抛出一个已检查的异常(checked exceptions),并且这些异常可以被捕获和处理。
  3. 使用场景
    • Runnable 适用于那些不需要返回值,且不会抛出已检查异常的情况,如简单的打印输出或修改共享变量。
    • Callable 适用于那些需要返回值或者需要抛出已检查异常的情况,如处理计算结果、进行网络或IO操作等。
  4. 与Future结合
    • Runnable 任务不能直接与 Future 一起使用来获取异步执行的结果。
    • Callable 任务可以与 Future 一起使用,通过 Future 可以获取异步执行的结果,并可以判断任务是否完成、取消任务等。

总结来说,Callable 可以看作是 Runnable 接口的补充,它提供了返回值和异常处理的能力,更加适合于复杂的异步编程场景。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值