线程学习(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

目录
相关文章
|
2月前
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
Java多线程同步大揭秘:synchronized与Lock的终极对决!
58 5
|
2月前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
Java并发编程实战:使用synchronized关键字实现线程安全
35 0
|
1月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
102 6
【Java学习】多线程&JUC万字超详解
|
2月前
|
安全 Java 开发者
Java多线程同步:synchronized与Lock的“爱恨情仇”!
Java多线程同步:synchronized与Lock的“爱恨情仇”!
81 5
|
2月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
20 2
|
2月前
|
Java 测试技术
Java多线程同步实战:从synchronized到Lock的进化之路!
Java多线程同步实战:从synchronized到Lock的进化之路!
86 1
|
2月前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
2月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
2月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
52 1
|
2月前
|
Java C++
【Java 并发秘籍】synchronized vs ReentrantLock:揭秘线程同步神器的对决!
【8月更文挑战第24天】本文详细对比了Java并发编程中`synchronized`关键字与`ReentrantLock`的不同之处。`synchronized`作为内置关键字,提供自动锁管理但不支持中断或公平锁;`ReentrantLock`则通过显式调用方法控制锁,具备更多高级功能如可中断、公平锁及条件变量。文章通过两个计数器类实例展示了两种机制的具体应用,帮助读者理解其差异及适用场景。掌握这两者对于提升多线程程序设计能力至关重要。
41 0