线程学习(2)线程创建,等待,安全,synchronized(一)

简介: 线程学习(2)线程创建,等待,安全,synchronized(一)

💕"i need your breath"💕

作者:Mylvzi

文章主要内容:线程学习(2)

前情回顾:

 在上一篇博客中介绍到了进程与线程的区别,以及初步了解如何在Java实现多线程编程,通过内置的Thread类来实现多线程,充分利用多核cpu资源,要充分认识到每一个线程都是一个独立的"执行流",本篇文章继续讲解和Thread有关的一些操作

一.Thread类的创建方式

1.继承Thread  重写run

//创建一个类  继承于Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        // 线程的入口  告诉线程要执行哪些逻辑
            System.out.println("hello thread");
            try {
                // 休眠1s
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        // 首先要实例化出一个Thread类
        Thread thread = new MyThread();
//         start和run都是Thread类的成员
//         run只是告诉线程要去执行那些逻辑
//         start是真正的调用系统的api,创建出一个线程,再让线程去执行run
        thread.start();
//        thread.run();
        while (true) {
            System.out.println("hello main");
            // 休眠1s
            Thread.sleep(1000);
        }
    }
}

2.实现Runnable  重写run

 创建自定义类时让其实现Runnable接口,这样写的原因本质在于Thread类也实现了Runnable接口

class MyThread implements Runnable {
    @Override
    public void run() {
        while(true) {
            System.out.println("==");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        // 使用向上转型 是Java生态中的常见方式
        // 先实现一个Runnable接口
        Runnable runnable = new MyThread();
        Thread thread = new Thread(runnable);
        thread.start();
        while (true) {
            System.out.println("==");
            Thread.sleep(1000);
        }
    }
}

说明:

Runnable表示的是一个"可以运行的任务",这个任务是交给线程执行还是交给其他是体执行,Runnable本身并不关心~

Runnable接口用来表示一个可以在线程中单独执行的任务,一个类只要实现了Runnable接口并且实现他的run方法,那么这个类的实例就能够单独在线程中执行,Runnable接口就像是一个点石成金的"魔法师",只要被他修饰过,就具有了"可被执行"的属性,这个任务不仅仅可以通过线程来执行,也可以通过线程池和执行器来执行

使用Runnable接口有哪些好处呢?直接继承Thread类不是更简单么?使用Runnable接口最大的好处就是可以"解耦合",降低代码之间的联系性,代码之间的联系性越高,耦合度就越高;反之亦然,耦合度过高不利于我们之后对代码进行修改~就像你和你最好的哥们一起创业,分钱肯定是不好分的~

上述两种创建Thread类的方式有所不同,第一种是直接通过MyThread类来实例化一个Thread类,第二种是先通过MyThread类先实例化一个Runnable接口,再通过这个接口去实例化一个Thread类。为什么第二种方式耦合度更低呢?原因在于第二种方式自定义类和Thread类之间的联系性降低了,他们之间是通过Runnable接口来联系起来的,以后使用更多线程的时候就都可以通过Runnable这个接口来实现,请看第二种方式创建线程的图解

第一种方式的图解

很明显第二种方式代码之间的耦合性更低

3.继承Thread,重写run,使用匿名内部类

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        // 继承Thread  使用匿名内部类
        Thread t = new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t.start();
        while (true) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

4.实现Runnable  重写run,使用匿名内部类

public class Demo11 {
    public static void main(String[] args) {
        // 实现Runnable  重写run  使用匿名内部类
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread t = new Thread(runnable);
        t.start();
    }
}

5.使用lambda表达式+Runnable接口(推荐方式)

Runnable接口是一个函数式接口,只有一个抽象方法run,所以可以使用lambda表达式来实现

public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        // 使用lambda表达式
        Runnable runnable = () -> {
            while (true) {
                System.out.println("Mythread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        while (true) {
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

使用这种方式创建线程,代码既简洁,又优雅,耦合性也低,推荐大家使用这种方式创建线程

Thread类的其他构造方法

Thread(String name) 创建线程对象,并命名

这个构造方法主要用于给线程命名,方便后续进行调试

// 可以为线程起一个名字作为标识  对线程的执行没有影响  就是单纯的一个"标识"  方便之后调试进行检查区分
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"这是一个线程名字");

Thread(Runnable target, String name) 使用 Runnable 对象创建线程对象,并命名 【了解】Thread(ThreadGroup group, Runnable target) 线程可以被用来分组管理,分好的组即为线程组

二.Thread类的一些属性

1.ID

线程的唯一标识,是Java为每个线程分配的"身份标识"

获取方法

getId()

Thread t = new Thread();
        long tid = t.getId();// 返回值是一个长整型
        System.out.println("线程ID:" + tid);// 输出线程ID:20

2.名称name

就是线程的名字,便于后序进行调试

获取方法

getName()

Thread t = new Thread("我是线程");
        String tName = t.getName();
        System.out.println(tName);// 输出我是线程

注意:此方法在源码中是被final修饰的,意味着子类无法重写方法

3.状态 state

进程最常见的两种状态是就绪状态和阻塞状态,线程也有自己的一些属性

// 获取线程的所有状态
        for (Thread.State state : Thread.State.values()) {
            System.out.print(state+" ");
        }

  • NEW  Thread  对象已经存在 但是还没有通过start方法调用
  • RUNNABLE  就绪状态  线程已经在cpu上执行/等在在cpu上执行
  • TERMINATED  Thread对象还在  但系统内核中的线程不存在
  • TIMED_WAITING  阻塞 由于sleep这种固定时间的方式产生的阻塞
  • WAITING  阻塞 由于wait这种不固定时间的方式产生的阻塞
  • BLOCKED  阻塞  由于锁竞争导致的阻塞
Thread t = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println(t.getState());// Thread类存在,但是还没有调用start方法,状态为NEW
        t.start();
        System.out.println(t.getState());// RUNNABLE
        Thread.sleep(3000);
        System.out.println(t.getState());// TERMINATED

4.优先级priority

获取线程的优先级

获取方法

getPriority

Thread t = new Thread("我是线程");
        int tPriority = t.getPriority();
        System.out.println(tPriority);

说明:其实此方法很"鸡肋",因为线程的优先级是由cpu的调度器决定的,在我们写代码的过程中很少去关注优先级,一是我们根本就观察不到,二是根本也没这个必要

线程学习(2)线程创建,等待,安全,synchronized(二)+https://developer.aliyun.com/article/1413578

目录
相关文章
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
Java多线程同步大揭秘:synchronized与Lock的终极对决!
191 5
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
Java并发编程实战:使用synchronized关键字实现线程安全
166 0
|
8月前
|
Java 调度 开发者
Java线程池ExecutorService学习和使用
通过学习和使用Java中的 `ExecutorService`,可以显著提升并发编程的效率和代码的可维护性。合理配置线程池参数,结合实际应用场景,可以实现高效、可靠的并发处理。希望本文提供的示例和思路能够帮助开发者深入理解并应用 `ExecutorService`,实现更高效的并发程序。
185 10
|
9月前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
203 7
|
9月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
289 3
|
9月前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
9月前
|
Java 调度
【JavaEE】——线程的安全问题和解决方式
【JavaEE】——线程的安全问题和解决方式。为什么多线程运行会有安全问题,解决线程安全问题的思路,synchronized关键字的运用,加锁机制,“锁竞争”,几个变式
|
10月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
114 4
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
1006 6
【Java学习】多线程&JUC万字超详解
|
11月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
76 2