Java 基础之多线程

目录

一、创建线程方式一:继承Thread创建

二、创建线程方式二:实现Runnable接口

三、线程状态

四、线程调度

五、线程同步

六、常用的线程安全类


 

一、创建线程方式一:继承Thread创建

1、优点:简单直观

        继承 Thread 类并重写 run() 方法,可以使线程的创建和管理相对简单。

        可以直接调用 start() 方法来启动线程。

2、缺点

        Java 不支持多继承,因此如果已经继承了其他类,就无法再通过继承 Thread 类来创建线程。

        继承 Thread 类的方式将线程和任务逻辑绑定在一起(耦合),不利于代码的组织和复用。

3、示例代码

   
    public static void main(String[] args) {

        TestThread thread1 = new TestThread();
        TestThread thread2 = new TestThread();
        thread1.start();
        thread2.start();

    }


    public static class TestThread extends Thread{
        @Override
        public void run() {
            super.run();

            for(int count = 0; count < 5; count++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-"+count);
            }
        }
    }

二、创建线程方式二:实现Runnable接口

1、优点:支持多继承、任务逻辑与线程逻辑分离、适合线程池等场景

        由于 Java 中的类可以实现多个接口,因此使用 Runnable 接口可以避免继承 Thread 类的单继承限制。

        使用 Runnable 接口,你可以将任务逻辑独立出来,便于任务的复用和组织。

        线程池中通常使用 Runnable 接口来表示要执行的任务。

2、缺点

        使用时需要借助 Thread 类来创建线程,稍显繁琐一些。

3、示例代码

    
    public static void main(String[] args) {

        TestRunnable runnable1 = new TestRunnable();
        TestRunnable runnable2 = new TestRunnable();

        Thread thread1 = new Thread(runnable1, "线程1");
        Thread thread2 = new Thread(runnable2, "线程2");
        thread1.start();
        thread2.start();

    }


    public static class TestRunnable implements Runnable {
        @Override
        public void run() {
            for (int count = 0; count < 5; count++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "-" + count);
            }
        }
    }

三、线程状态

线程可以具有不同的状态,表示线程在执行过程中的不同阶段和状态。

(1)New(新建)

当创建一个线程对象但尚未调用 start() 方法时,线程处于新建状态。在这个状态下,线程已经被创建,但尚未分配系统资源或开始执行。

(2)Runnable(可运行/就绪)

一旦调用了线程对象的 start() 方法,线程进入可运行状态。在这个状态下,线程已经分配了系统资源,可以被调度为运行状态,但实际上可能还没有开始执行,因为线程调度器可能会决定何时运行该线程。

(3)Running(运行)

当线程被线程调度器选中并开始执行时,线程处于运行状态。在这个状态下,线程的代码正在执行。

(4)Blocked(阻塞)

线程在某些情况下可能会被阻塞,暂时停止执行。当线程在等待某些条件满足时,比如等待获取一个锁或等待某个事件发生,就会进入阻塞状态。一旦条件满足,线程将从阻塞状态转移到可运行状态,等待线程调度器分配执行时间。

(5)Waiting(等待)

当线程被要求等待某些特定条件时,它会进入等待状态。这可以通过调用 Object 类的 wait() 方法或类似的等待方法来实现。在等待状态下,线程会释放它持有的锁,并等待其他线程通过 notify() 或 notifyAll() 方法来唤醒它。

(6)Timed Waiting(定时等待)

类似于等待状态,但在等待一段时间后会自动唤醒。这可以通过调用带有超时参数的等待方法来实现,如 wait(long timeout) 方法。

(7)Terminated(终止)

当线程的 run() 方法执行完毕或因异常而终止时,线程将进入终止状态。在终止状态下,线程不再执行任何代码。

四、线程调度

1、判断线程是否活动 isAlive()

判断一个线程是否还存活(即是否还在运行中)。返回 true 表示线程还在运行,返回 false 表示线程已经终止。

1、优先级 setPriority(int priority)

线程调度器会根据线程的优先级来确定哪个线程应该先执行。优先级范围是从 1 到 10,其中 1 是最低优先级,10 是最高优先级。默认值为5。

2、休眠 sleep(long millis)

使当前线程进入指定的毫秒数的休眠状态。在休眠期间,线程不会执行任何操作,可以用来模拟等待时间或者为其他线程让出执行时间。

3、等待和协作 join()

在一个线程内部调用另一个线程的 join() 方法,会等待另一个线程执行完毕后再继续执行当前线程。这个方法通常用于实现线程的等待和协作,比如等待子线程完成。

4、让出CPU执行时间 yield()

调用 yield() 方法会让当前线程主动让出 CPU 执行时间,让线程调度器决定下一个执行哪个线程。这是一种提高线程协作的方式,不过它并不保证一定会生效,具体取决于线程调度器的实现。

5、中断 interrupt()

向目标线程发送一个中断信号,用于请求目标线程停止执行。被中断的线程可以在适当的时候检查自己是否被中断,并作出响应。

测试代码:

    
    public static void main(String[] args) {

        Thread thread = Thread.currentThread();

        System.out.println("线程名称:"+thread.getName());
        thread.setName("my-thread");
        System.out.println("线程名称:"+thread.getName());


        TestThread thread1 = new TestThread();
        TestThread thread2 = new TestThread();
        thread1.setName("线程11");
        thread2.setName("线程22");
        thread2.setPriority(10);//设置优先级
        thread1.start();
        thread2.start();

        //thread1.isAlive(); //判断线程1是否还存活

        try {
            // 让主线程行等待 线程1 和 线程2 执行完成
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    public static class TestThread extends Thread{
        @Override
        public void run() {
            super.run();

            Thread.yield(); //让当前线程主动让出 CPU 执行时间

            for(int count = 0; count < 100; count++) {
                try {
                    Thread.sleep(1);//休眠1毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"-"+count);
            }
        }
    }

五、线程同步

synchronized 是 Java 中用于实现线程同步的关键字,它可以应用于方法或代码块。synchronized 的主要作用是确保在多线程环境中对共享资源的访问是线程安全的,防止多个线程同时访问和修改共享资源而导致的并发问题。

需要注意的是,过度使用 synchronized 可能会导致性能问题,因为它会引入线程之间的竞争和上下文切换开销。因此,在使用 synchronized 时,需要谨慎考虑并发性能问题,确保只在必要的情况下使用它,同时考虑使用更轻量级的同步机制,如 java.util.concurrent 包中提供的并发工具。

测试代码:

  
    public static void main(String[] args) {

        TestRunnable runnable = new TestRunnable();

        Thread thread1 = new Thread(runnable, "线程1");
        Thread thread2 = new Thread(runnable, "线程2");
        thread1.start();
        thread2.start();

    }


    public static class TestRunnable implements Runnable {
        private int order = 0;

        @Override
        public void run() {
            for (int count = 0; count < 10; count++) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //this.Test1(); //不加synchronized,输出序号不按顺序增加
                //this.Test2(); //在方法上加synchronized,来同步
                this.Test3(); //在代码块上加synchronized,来同步
            }
        }

        private void Test1() {
            order++;
            System.out.println(Thread.currentThread().getName() + " 执行=" + order);
        }

        private synchronized void Test2() {
            order++;
            System.out.println(Thread.currentThread().getName() + " 执行=" + order);
        }

        private void Test3() {
            synchronized (this) {
                order++;
                System.out.println(Thread.currentThread().getName() + " 执行=" + order);
            }
        }
    }

六、常用的线程安全类

1、字符串处理类StringBuffer。

2、集合Hashtable、Vector,但是在新版中已过时,推荐使用ConcurrentHashMap、CopyOnWriteArrayList 来代替。

3、想要使用线程安全的集合类尽量使用java.util.concurrent包下的所有类。

 有关集合使用相关问题请参考Java 基础之集合

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QIFU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值