多线程之并发基础(三)

简介: 线程是轻量级的进程,进程可以说是线程的容器。线程是程序执行的最小单位。使用多线程而不是使用多进程进行并发程序的设计,因为线程的切换和调度成本远远小于进程。

线程是轻量级的进程,进程可以说是线程的容器。线程是程序执行的最小单位。使用多线程而不是使用多进程进行并发程序的设计,因为线程的切换和调度成本远远小于进程。

img_cfd7d02f4c974f7b4f92fb93fd6a41a2.png
与文无关

本文知识点目录:

  • 线程的状态
  • 线程的常见操作
    • Daemon线程
    • 线程优先级
    • wait与notify

线程的状态

在Java程序中,线程有如下状态

  • NEW(新建): 新的线程刚刚创建,还没有启动。

  • Runnable(就绪):线程已经调用了start方法,正在被JVM执行的时候,也有可能正在等待某个操作系统的资源。

  • Blocked(阻塞):有其它线程占着锁不释放,当前线程阻塞与锁

  • Waiting(等待):表示当前线程需要等待其它线程做出一些特定动作(通知或中断),当调用以下方法时会进入此状态。
    Object.wait()没有时间参数
    Thread.join()没有超时参数
    LockSupport.park方法

  • Timed Waiting(等待并计时):指定时间waiting,一边等,一边计时,一般调用如下方法可以j进入此状态。
    Thread.sleep
    Object.wait()带有参数
    Thread.join ()带有超时参数
    LockSupport.parkNanos
    LockSupport.parkUntil

  • Terminated(终止) 线程完成了它的操作,也有可能错误的执行而终止。

img_0e60396abad5286fa6200ba5ca3cf896.png
线程状态图
img_deff86d94548a712b727e5e09d0b6af2.png
线程状态图

线程操作

实现多线程

我们想要实现多线程常用的有两种方法

  • 实现Runnable接口
  • 继承自Thread线程

java只能单继承,因此如果是采用继承Thread的方法,那么在以后进行代码重构的时候可能会遇到问题,因为你无法继承别的类了。

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

注意这是在多线程情况下, 每个人运行的结果可能都不一样。要想保住线程安全性,还需要额外的操作。

// 案例代码,继承线程变量无法共享
public class ThreadOrRunnable extends Thread {
    private int count=5;
    private String name;
    public ThreadOrRunnable(String name) {
        this.name=name;
    }

    @Override
    public void run() {
//        super.run();
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(name +  "运行 count=" + count);
        }
    }

    public static void main(String[] args) {
        ThreadOrRunnable threadOrRunnable = new ThreadOrRunnable("A");
        ThreadOrRunnable threadOrRunnableB = new ThreadOrRunnable("B");
        threadOrRunnable.start();
        threadOrRunnableB.start();
        
    }
}
//运行结果:
A运行 count=4
A运行 count=3
A运行 count=2
A运行 count=1
B运行 count=4
A运行 count=0
B运行 count=3
B运行 count=2
B运行 count=1
B运行 count=0
// 案例 继承自Runnable可以共享count变量
public class RunnableTest implements Runnable{
    private int count=15;
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            count--;
            System.out.println(Thread.currentThread().getName() + "运行: count=" + count);
        }
    }

    public static void main(String[] args) {
        RunnableTest runnableTest = new RunnableTest();
        new Thread(runnableTest,"A").start();
        new Thread(runnableTest,"B").start();
    }
}

// 运行结果
B运行: count=13
A运行: count=13
B运行: count=12
A运行: count=11
B运行: count=10
A运行: count=9
A运行: count=7
A运行: count=6
B运行: count=8
B运行: count=5
Daemon线程

Daemon线程是一种支持型线程,在后台调度及支持性工作。它有两个点需要注意:

  • 必须在程序调用start方法之前调用Thread.setDaemon(true)方法才能设置为daemon线程,如果start()之后设置,会报IllegalThreadStateException异常
  • 在所有前台线程执行完毕的时候,daemon线程会自动销毁。
Thread thread = new Thread();
thread.setDaemon(true);
thread.start();
线程优先级

Java中的线程可以有自己的优先级,优先级高的线程在竞争资源时更可能抢占资源,但是这只是一个概览问题,并不是谁的优先级高,谁就一定先执行。

Thread thread = new Thread();
 // MIN_PRIORITY = 1;
 // NORM_PRIORITY = 5;
 // MAX_PRIORITY = 10;
thread.setPriority(int newPriority) 
thread.start();
wait与notify

wait和notify方法不是在Thread类中的,而是在Object类中,意味着任何对象都可以调用这两个方法。

当一个线程A调用了obj.wait()方法,那么线程A就会停止继续执行,而是转为waiting状态。一直都其它线程调用obj.notify()方法为止。

img_d3c3b8a1d0305cbff8e96084d810f825.png
notify与notifyAll

注意:

  • notify方法是从等待队列中的线程随机选择一个,我们无法保证它唤醒的是那一个。notifyAll()方法会唤醒所有等待的线程。
  • object.wait()方法必须包含在synchronized语句中,wait或notify都需要首先获得目标对象的监视器。

Object.wait和Thread.sleep方法都可以让线程等待若干时间,它们哟徐诶区别。

  • wait可以被唤醒,sleep不可以
  • wait会释放目标对象的锁,而sleep不释放任何锁。
public class WaitNotify {
    static volatile boolean flag = true;
    static Object lock = new Object();

    static class Wait implements Runnable{

        @Override
        public void run() {
            synchronized (lock){
                while (flag){
                    System.out.println(Thread.currentThread().getName() +  " 时间: " + System.currentTimeMillis());
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //条件满足的时候完成了工作
                System.out.println("flag=" + flag + "任务完成");
            }
        }
    }

    static class Notify implements Runnable{

        @Override
        public void run() {
            synchronized (lock){
                System.out.println(Thread.currentThread().getName() + " 持有锁");
                lock.notifyAll();
                flag = false;
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            synchronized (lock){
                System.out.println("再次持有锁");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(),"waitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(),"notifyThread");
        notifyThread.start();
    }
}

//运行结果,调用notify之后,wait状态会变为blocked状态,然后再进入Runnable状态。


img_99b133a2009137723a8731c8c2ae011b.gif
运行结果

Wait与notify可以提炼出等待/通知的经典范式,氛围两部分:
等待方规则:

  1. 获取对象的锁
  2. 如果条件不满足,那么调用wait方法,被通知后仍要检查条件
  3. 条件满足则执行相应的逻辑

通知方规则:

  1. 获得对象的锁
  2. 改变条件
  3. 通知等待对象上的线程。

问题

关于Object.wait与notify,最后再留下来一个问题:
为什么wait与notify方法要在Object类中调用而不是在Thread中?

最后

这次提到了一些Thread的基本概念,线程的状态切换,线程的两个属性,最后提了一下wait与notify方法。下次讲一下线程的常见用法。

参考

目录
相关文章
|
15天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
1天前
|
安全 C++
C++多线程编程:并发与同步
C++多线程编程:并发与同步
7 0
|
4天前
|
安全 Java
Java中的并发编程:理解并发性与线程安全
Java作为一种广泛应用的编程语言,在并发编程方面具有显著的优势和特点。本文将探讨Java中的并发编程概念,重点关注并发性与线程安全,并提供一些实用的技巧和建议,帮助开发人员更好地理解和应用Java中的并发机制。
|
10天前
|
算法 安全
AtomicInteger使用非阻塞算法,实现并发控制多线程实现售票
AtomicInteger使用非阻塞算法,实现并发控制多线程实现售票
|
16天前
|
设计模式 Java 编译器
深入理解Java中的多线程并发控制
Java作为一种流行的编程语言,其多线程并发控制机制一直是开发者关注的焦点。本文旨在通过探讨Java中的多线程并发控制原理、常用同步工具及设计模式,帮助读者深入理解并有效应用多线程并发控制技术,以提高程序性能和稳定性。
|
16天前
|
监控 安全 Java
一文讲明白Java中线程与进程、并发与并行、同步与异步
一文讲明白Java中线程与进程、并发与并行、同步与异步
8 1
|
21天前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
22天前
|
存储 Java Python
【Python小知识】如何解决代理IP在多线程环境下的并发问题?
【Python小知识】如何解决代理IP在多线程环境下的并发问题?
|
23天前
|
安全 算法 Java
Java中的多线程并发控制与同步机制
【4月更文挑战第17天】 在现代软件开发中,Java作为一种广泛使用的编程语言,其对多线程的支持是构建高性能应用程序的关键。本文将深入探讨Java中的多线程并发控制与同步机制,包括基本的线程创建、生命周期管理,以及高级的并发工具如synchronized关键字、ReentrantLock类、并发集合和原子变量等。通过理论分析与实例演示,旨在为读者提供一个清晰的多线程并发控制与同步的实现框架,并指出在实践中如何避免常见的并发问题,如死锁、竞态条件和资源争用等。
|
23天前
|
安全 Java 开发者
Java中的多线程并发控制与同步机制
【4月更文挑战第17天】在Java编程中,多线程是实现并行处理和提高程序性能的重要手段。然而,随之而来的线程安全问题和数据一致性问题不容忽视。本文深入剖析了Java中多线程的并发控制与同步机制,包括synchronized关键字、显式锁Lock以及并发集合等高级特性。通过对比分析这些机制的原理和使用场景,旨在帮助开发者理解并合理运用于实际项目中,以解决并发环境下的数据竞争和资源冲突问题。

相关实验场景

更多