Java的多线程1:线程的使用

简介:

Java的多线程1:线程的使用

概述
进程是线程的容器,线程共享进程的内存空间,所以线程之间彼此通信是比较容易的,而线程又有自己私有的内存地址,其他线程无法访问。了解进程和线程关系,可以看我另一篇博客《进程与线程》

Java创建线程的两种方式
继承Thread类

public class ThreadDemo1 extends Thread {

@Override
public void run(){
    for (int i = 0; i < 10; i++) {
        System.out.println("当前执行的线程是" + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
    ThreadDemo1 threadDemo1 = new ThreadDemo1();
    ThreadDemo1 threadDemo2 = new ThreadDemo1();
    threadDemo1.start();
    threadDemo2.start();
}

}

执行结果是不确定的

实现Runnable

public class ThreadDemo2 implements Runnable {

@Override
public void run() {
    for (int i = 0; i < 10; i++) {
        for (int j = 0;j < 1000; ++j){
            System.out.println(i + "当前执行的线程是" + Thread.currentThread().getName());
        }
    }
}

public static void main(String[] args) {
    ThreadDemo2 threadDemo1 = new ThreadDemo2();
    ThreadDemo2 threadDemo2 = new ThreadDemo2();
    Thread thread1 = new Thread(threadDemo1);
    Thread thread2 = new Thread(threadDemo2);
    thread1.start();
    thread2.start();
    System.out.println("当前线程是===>" + Thread.currentThread().getName());
}

}

主线程的名字为main,非主线程的名字是由虚拟机来指定的,同时,我们也可以为线程指定具体的名称。

我们保证每个线程都能正常启动,并不意味着它会按顺序的执行,因为调度程序是无法保证它的执行次序的,同时,run()函数结束时,意味着该线程的任务完成了。

注意:调用线程要调用start,如果调用run,那仅仅是简单的对象调用。

线程生命周期

public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}

新建状态

线程对象创建后,就进入新建状态  Thread thread = new Thread

就绪状态

调用start()方法,线程进入就绪状态,但并不意味着线程就立即执行,只是说明此线程已经做好准备,随时等待CPU调度执行。

阻塞状态

多个线程同时竞争一个独占锁,其他未抢到锁的线程,就进入阻塞状态被放置到锁池中,直到获取到锁,进入就绪状态

等待状态

该线程需要等待其他线程做出一些特定动作,通知或者是中断,等待其被其他线程唤醒,像CountDownLatch就可以等待一个或者几个线程结束。

超时等待状态

和等待状态不同的是,它可以在指定的时间自行的返回,sheep(long)函数就会让线程进入超时等待状态,时间到了才会转入就绪状态。

运行状态(Running)

CPU调度处于就绪状态的线程时,这个线程才真正执行,进入运行状态。

终止状态

线程正常执行完毕后或提前强制性终止或出现异常,线程就要销毁,释放资源。

线程的方法调用
获取线程基本信息

public class ThreadDemo6 {

public static void main(String[] args) {
    Thread thread = new Thread(){
        @Override
        public void run(){
            /*获取线程唯一id标识*/
            long id = this.getId();
            System.out.println("thread的ID==>" + id);

            /*获取线程名字*/
            String name = this.getName();
            System.out.println("thread的名字==>" + name);

            /*获取线程的优先级 默认5 1-10*/
            int priority = this.getPriority();
            System.out.println("thread的优先等级==>" + priority);

            /*查看当前线程是否为守护线程*/
            boolean isDaemon = this.isDaemon();
            System.out.println("thread是否为守护线程==>" + isDaemon);

            /*查看线程是否被中断*/
            boolean isInterrupted = this.isInterrupted();
            System.out.println("thread是否被中断==>" + isInterrupted);
        }
    };
    thread.start();
}

}

执行结果

thread的ID==>11
thread的名字==>Thread-0
thread的优先等级==>5
thread是否为守护线程==>false
thread是否被中断==>false
Thread.yield()

public class ThreadDemo1 implements Runnable {

protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
    this.countDown = countDown;
}
public String status(){
    return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
@Override
public void run() {
    while (countDown-- > 0){
        System.out.println(status() + "  ");
        Thread.yield();
    }
}

public static void main(String[] args) {
    for (int i = 0; i < 3; i++){
        new Thread(new ThreadDemo1()).start();
    }
}

}

0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(stop!)

1(9)#1(8)#1(7)#1(6)#1(5)#1(4)#1(3)#1(2)#1(1)#1(stop!)

2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#2(stop!)

这个是一个倒计时的任务,对Thread.yield()调用是对线程调度器的一种建议,它在声明“我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机”,说白就是自己按暂停键,让出自己CPU的使用权限,转为就绪状态,至于下一次什么时候能获得CPU调度就不一定了,有时很快,有时得等上一会。

Thread.sleep

public class ThreadDemo1 implements Runnable {

protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
    this.countDown = countDown;
}
public String status(){
    return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
@Override
public void run() {
    try {
        while (countDown-- > 0){
            System.out.println(status());
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

public static void main(String[] args) {
    for (int i = 0; i < 3; i++){
        new Thread(new ThreadDemo1()).start();
    }
}

}

Thread.sleep(long)将使“正在执行的任务“中止执行给定的时间(暂停执行)并且让出CPU使用权,这个语句相当于说在接下来的1秒内,你都不要叫我,我想睡一会,1秒睡眠时间过后,它自动转为就绪状态,但CPU不一定马上执行这个睡醒的线程,这要取决于是否抢到CPU时间片段。值得注意的是如果sleep和yield上下文被加锁了,它们依然使用锁,不会去释放。而sleep与yield最大的不同是,yield不会让线程进入等待状态,只是把线程转为就绪状态,并把CPU执行机会让步给优先级相同或者更高的线程,而sleep能控制具体交出CPU的使用时间。

Thread.currentThread()

public class ThreadDemo2 extends Thread {

static {
    System.out.println("静态块执行的线程===>" + Thread.currentThread().getName());
}
{
    System.out.println("非静态块执行的线程是====>" + Thread.currentThread().getName());
    System.out.println("this.getName()1=====>" + this.getName());
}

public ThreadDemo2(){
    System.out.println("构造方法内执行的线程====>" + Thread.currentThread().getName());
    System.out.println("this.getName()2=====>" + this.getName());
}

@Override
public void run() {
    System.out.println("当前执行的线程为====>" + Thread.currentThread().getName());
    System.out.println("this.getName()3=====>" + this.getName());
}

public static void main(String[] args) {
    ThreadDemo2 threadDemo2 = new ThreadDemo2();
    threadDemo2.start();
}

}

执行结果

静态块执行的线程===>main
非静态块执行的线程是====>main
this.getName()1=====>Thread-0
构造方法内执行的线程====>main
this.getName()2=====>Thread-0
当前执行的线程为====>Thread-0
this.getName()3=====>Thread-0

currentThread返回的是当前正在执行线程对象的引用,它与this.getName()有明显的不同,执行静态块,非静态块,构造方法的线程是main,而非ThreadDemo2,在执行run()方法的才是实例化的线程threadDemo2。所以在当前执行的Thread未必就是Thread本身。

isAlive()

public class ThreadDemo3 extends Thread {

@Override
public void run(){
    System.out.println("执行执行====" + this.isAlive());
}

public static void main(String[] args) {
    ThreadDemo3 threadDemo3 = new ThreadDemo3();
    System.out.println("begin===>" + threadDemo3.isAlive());
    threadDemo3.start();
    System.out.println("end==>" + threadDemo3.isAlive());
}

}

begin===>false
end==>true
执行执行====true
isAlive()检测线程是否处于活动状态,活动状态返回true

setPriority()

优先级设定,优先级高的线程越容易获取CPU使用权,

public class ThreadDemo4 {

public static void main(String[] args) {
    for (int i = 0; i < 5; ++i){
        Thread1 thread1 = new Thread1();
        thread1.setPriority(6);
        Thread2 thread2 = new Thread2();
        thread2.setPriority(4);
        thread2.start();
        thread1.start();
    }
}

}
class Thread1 extends Thread{

@Override
public void run(){
    for (int i = 0; i < 100000; ++i){
        System.out.println("+++++++++++++");
    }
}

}
class Thread2 extends Thread{

@Override
public void run(){
    for (int i = 0; i < 100000; ++i){
        System.out.println("--------------");
    }
}

}

执行结果

+++++++++++++
+++++++++++++
+++++++++++++
+++++++++++++
+++++++++++++
+++++++++++++
+++++++++++++
+++++++++++++
...

CPU会将资源尽量让给优先级高的线程

setDaemon()

守护线程,也有人叫后天线程,我们创建出来的线程默认都是前台线程,在使用上来说,守护线程和前台线程是没啥区别,区别在于进程结束,当一个进程中的所有前台线程都结束时,无论这个进程中的守护线程是否还在运行都要强制将他们结束。也就是说前台线程都结束了,守护线程也会自动销毁,它是为其他线程提供便利而存在的。

/rose与jack/
public class ThreadDemo5 {

public static void main(String[] args) {
    Rose rose = new Rose();
    Jack jack = new Jack();
    /*设置为守护线程必须在线程未启动之前*/
    jack.setDaemon(true);
    rose.start();
    jack.start();
}

}
class Rose extends Thread{

@Override
public void run(){
    for (int i = 0; i < 5; ++i){
        System.out.println("rose: let me go!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("成功跳水");
}

}
class Jack extends Thread{

@Override
public void run(){
    while (true){
        System.out.println("jack:you jump! i jump!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}

执行结果

rose: let me go!
jack:you jump! i jump!
rose: let me go!
jack:you jump! i jump!
rose: let me go!
jack:you jump! i jump!
rose: let me go!
jack:you jump! i jump!
rose: let me go!
jack:you jump! i jump!
成功跳水

jack守护着rose,jack是守护线程,当rose跳水后,jack认为自己也没有活着的必要了,也自己销毁了,但注意一点是这当中还有一个第三者main,需要main也运行完jack线程才会销毁。

join()

这个方法可以协调多个线程同步运行,多线程运行本身是设计异步运行的,但在程序运行业务中,有可能线程A的计算需要线程B的返回结果,这就需要他们执行各自任务时要有先后,join就需要协调这些线程同步运行。

public class ThreadDemo6 {

private static boolean isFinish = false;

public static void main(String[] args) {
    Thread download = new Thread(){
        @Override
        public void run(){
            System.out.println("下载图片中.....");
            for (int i = 1; i <= 100; ++i){
                System.out.println("下载进度" + i + "%");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("图片下载完毕");
            isFinish = true;
        }
    };
    Thread show = new Thread(){
        @Override
        public void run(){
            System.out.println("开始显示图片...");
            try {
                download.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!isFinish){
                throw new RuntimeException("图片下载出错");
            }
            System.out.println("图片正常展示。。。");
        }
    };
    download.start();
    show.start();
}

}

执行结果

下载图片中.....
开始显示图片...
下载进度1%
下载进度2%
...
下载进度100%
图片下载完毕
图片正常展示。。。

show调用join会使show无限阻塞,直到down线程销毁为止,它和sleep最大的区别是join会释放锁,而sleep不会。

涉及到jmm内存模型,线程安全等,后面在介绍

原文地址https://www.cnblogs.com/dslx/p/12664004.html

相关文章
|
10天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
12天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
12天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
13天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3
|
13天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
93 2
|
13天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
37 1
|
21天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
29天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
29天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
50 3
|
5月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
142 1