首页 > 其他分享 >并发相关笔记一

并发相关笔记一

时间:2023-05-28 22:12:10浏览次数:36  
标签:Thread thread start 笔记 并发 线程 new 相关 ticket

并发和并行

并行:指两个或多个时间在同一时刻发生(同时发生);
并发:指两个或多个事件在一个时间段内发生。
在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。

进程与线程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程
线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。
注意:
1、因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性
2、Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
3、由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。

多线程的创建

继承Thread

    public static void main(String[] args) throws IOException {
        SubThread subThread1=new SubThread();
        SubThread subThread2=new SubThread();
        //调用线程的start(),启动此线程;调用相应的run()方法
        subThread1.start();
        subThread2.start();
        //一个线程只能够执行一次start(),start()中会判断threadStatus的状态是否为0,不为0则抛出异常
        //subThread1.start();
        //不能通过Thread实现类对象的run()去启动一个线程,此时只是主线程调用方法而已,并没有启动线程
        //subThread1.run();
        for (int i=0;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
	//1.创建一个继承Thread的子类
	class SubThread extends Thread{
    	//2.重写run方法,方法内实现此子线程要完成的功能
    	@Override
    	public void run(){
        	for (int i=0;i<=100;i++){
            	System.out.println(Thread.currentThread().getName()+":"+i);
        	}
    	}
	}

image
如图所示,还可以看到线程的随机性
区别:
①避免java单继承的局限性
②如果多个线程要操作同一份资源,更适合使用实现的方式

实现runnable接口

public class TreadDemo {
    public static void main(String[] args) throws IOException {
            //此程序存在线程的安全问题,打印车票时,会出现重票、错票,后面线程同步会讲到
            Window window=new Window();
            Thread thread1=new Thread(window,"窗口一");
            Thread thread2=new Thread(window,"窗口二");
            Thread thread3=new Thread(window,"窗口三");
            thread1.start();
            thread2.start();
            thread3.start();
    }
}

class Window implements  Runnable{
    int ticket=100;
    @Override
    public void run(){
        while (true){
            if(ticket > 0){
                try {
                   TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
            }else {
                break;
            }
        }
    }
}

实现callable接口

public class TreadDemo {
    public static void main(String[] args) throws IOException {
            //此程序存在线程的安全问题,打印车票时,会出现重票、错票,后面线程同步会讲到
        FutureTask<Integer> futureTask = new FutureTask<>(new CallDemo());
       new Thread(futureTask).start();
    }
}

class CallDemo implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ""+i);
        }
        return 0;
    }
}

与前者相比,会有相应的返回这,futask提供了一些阻塞方法来获取返回值

使用线程池

todo 后续进行演示

线程的同步

复现场景

public class TestThread2 {
    public static void main(String [] args){
        Window window=new Window();
        Thread thread1=new Thread(window,"窗口一");
        Thread thread2=new Thread(window,"窗口二");
        Thread thread3=new Thread(window,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements  Runnable{
    int ticket=50;
    @Override
    public void run(){
        while (true){
            if(ticket > 0){
                try {
                    TimeUnit.MILLISECONDS.sleep(200);//模拟卖票需要一定的时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
            }else {
                System.out.println("票卖完了");
                break;
            }
        }
    }
}

输出结构
image
此时发现会有重票和超卖现象

结果分析

  1. 当票号为10时:A线程、B线程、C线程同时进入到if(ticket > 0)的代码块中,A线程已经执行了打印输出语句,但是还没有做ticket--操作,ticket--不是原子操作
  2. 这时B线程就开始执行了打印操作,那么就会出现两个线程打印票数一样,即卖的是同一张票,当票号为1时:A线程、B线程,C线程同时进入到if(ticket > 0)的代码块中,A线程执行了打印语句,并且已经做完了ticket--操作,则此时ticket=0,判断和ticket--不是原子操作;B线程再打印时就出现了0的情况,同理C线程打印就会出现-1的情况。

解决方案

使用同步代码块

 synchronized (this){
                if(ticket > 0){
                    try {
                        TimeUnit.MILLISECONDS.sleep(200);//模拟卖票需要一定的时间
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
                }else {
                    System.out.println("票卖完了");
                    break;
                }
            }

这样可以保证判断和票数的计算是原子性操作
this需要保证唯一性,因为this指向该对象,所以目前是可以进行保证.

使用同步方法进行

    public void run(){
        while (true){
           sellTicket();
           if (ticket<1){
               break;
           }
        }
    }
    private synchronized void sellTicket(){
        if(ticket > 0){
            try {
                TimeUnit.MILLISECONDS.sleep(200);//模拟卖票需要一定的时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
        }else {
            System.out.println("票卖完了");
        }
    }

改方法保证卖票操作原子性

线程状态

线程是一个动态执行的过程,它也有从创建到死亡的过程

线程状态枚举

    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;
    }

相关状态详解:

  1. 新建状态(new):使用 new 创建一个线程,仅仅只是在堆中分配了内存空间
    新建状态下,线程还没有调用 start()方法启动,只是存在一个线程对象而已
    Thread t = new Thread();//这就是t线程的新建状态
  2. 可运行状态(runnable):新建状态调用 start() 方法,进入可运行状态。而这个又分成两种状态,ready 和 running,分别表示就绪状态和运行状态
    就绪状态:线程对象调用了 start() 方法,等待 JVM 的调度,(此时该线程并没有运行)
    运行状态:线程对象获得 JVM 调度,如果存在多个 CPU,那么运行多个线程并行运行
    注意:线程对象只能调用一次 start() 方法,否则报错:illegaThreadStateExecptiong
  3. 阻塞状态(blocked):正在运行的线程因为某种原因放弃 CPU,暂时停止运行,就会进入阻塞状态。此时 JVM 不会给线程分配 CPU,知道线程重新进入就绪状态,才有机会转到 运行状态。
    注意:阻塞状态只能先进入就绪状态,不能直接进入运行状态
    阻塞状态分为两种情况:
    ①、当线程 A 处于可运行状态中,试图获取同步锁时,却被 B 线程获取,此时 JVM 把当前 A 线程放入锁池中,A线程进入阻塞状态
    ②、当线程处于运行状态时,发出了 IO 请求,此时进入阻塞状态
  4. 等待状态(waiting):等待状态只能被其他线程唤醒,此时使用的是无参数的 wait() 方法
    ①、当线程处于运行状态时,调用了 wait() 方法,此时 JVM 把该线程放入等待池中
  5. 计时等待(timed waiting):调用了带参数的 wait(long time)或 sleep(long time) 方法
    ①、当线程处于运行状态时,调用了带参数 wait 方法,此时 JVM 把该线程放入等待池中
    ②、当前线程调用了 sleep(long time) 方法
  6. 终止状态(terminated):通常称为死亡状态,表示线程终止
    ①、正常终止,执行完 run() 方法,正常结束
    ②、强制终止,如调用 stop() 方法或 destory() 方法
    ③、异常终止,执行过程中发生异常
    join的相关用法
public class TestThread2 {
    public static void main(String [] args) throws InterruptedException {
        Thread thread = new Thread(new Window());
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ""+i);
            if (i==20){
                System.out.println(Thread.currentThread().getState()+"-===");
                thread.join();
                System.out.println(Thread.currentThread().getState()+"-===");
            }
        }
    }
}

class Window implements  Runnable{

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

线程的通信

标签:Thread,thread,start,笔记,并发,线程,new,相关,ticket
From: https://www.cnblogs.com/yx-blog/p/17437667.html

相关文章

  • 相关子查询(由不相关子查询转换思想)
    查询本部门最高工资的员工信息:emp:员工信息表deptno:部门号 sal:员工薪水不相关其中几条:select*fromempewheree.deptno=10andsal=(selectmax(sal)fromempwheredeptno=10)uninoselect*fromempewheree.deptno=20andsal=(selectmax(sal)fromempwher......
  • 「学习笔记」(扩展)中国剩余定理
    有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?该问题出自《孙子算经》,具体问题的解答口诀由明朝数学家程大位在《算法统宗》中给出:三人同行七十希,五树梅花廿一支,七子团圆正半月,除百零五便得知。\(2\times70+3\times21+2\times15=233=2\times......
  • 《HTML入门笔记2》
    HTML常用标签分别有:a标签、img标签、table标签、form标签、input标签等。a标签(特别常用)a标签即超级链接,又叫超链接。一个网站通常由多个页面构成,进入网站时首先看到的就是其首页,如果想从首页跳转至其他页面,就需要在首页相应的位置添加超级链接。a标签其基本语法格式如......
  • 用redis项目练习笔记,跟着黑马敲,并有自己的理解在里面
    点评中,优惠卷牵扯到的秒杀问题。超卖现象如果多线程同时执行会因为高并发,先查询再插入之间会有空档时间,发生超卖问题。可以使用悲观锁或者乐观锁解决,出于对性能的考虑,用到了乐观锁。乐观锁的实现,用到了数据库where语句多加一个条件。每次判断跟上次相同,(这样会造成大量的失......
  • 软件工程日报——《人间》读书笔记
    总结以下《人件》这本书中涉及到的几个概念和建议1、帕金森定律帕金森定律讲述了如下的定律:如果一个很平庸的人作了管理,那么摆在它面前的只有三条路:退位给有能力的人。使用比自己更优秀的属下。运用比自己还平庸的手下。第一条路和第二条路一般是个有欲望的人,都不会采取,......
  • 【ABAQUS文档笔记】实体单元
    来自ABAQUSDOCUMENT/GETTINGSTARTEDWITHABAQUS/CAE/USINGCONTINUUMELEMENTS单元公式和积分fullintegration“完全积分”是指当单元具有规则形状时,对单元刚度矩阵中的多项式项进行精确积分所需的高斯点数。对于六面体和四边形元素,“规则形状”意味着边缘是直的,并以直......
  • WPF 入门笔记 - 02 - 布局综合应用
    本篇博文对接上篇末尾处WPF常用布局控件的综合应用,为痕迹g布局控件介绍课后作业的一个思路方法。前言首先来谈一谈布局原则:WPF窗口只能包含一个元素(Window元素属于内容控件,内容控件只允许有一个子元素),所以我们得在窗口中放置一个容器,才能使我们的窗口放置更多的内容。所以......
  • Git日常使用技巧 - 笔记
    Git日常使用技巧-笔记Git是目前世界上最先进的分布式版本控制系统学习资料廖雪峰学习视频https://www.bilibili.com/video/BV1pX4y1S7Dq/?spm_id_from=333.337.search-card.all.click&vd_source=2ac127043ccd79c92d5b966fd4a54cd7Git命令在线练习工具https......
  • [CMake] CMake学习笔记
    自己的学习和使用总结,还不完善,不定时更新。一.简介cmake是一款高级编译配置工具;所有操作都是通过编译CMakeLists.txt来完成的;CMake官方全部推荐使用大写指令;学习目的:为将来处理大型的C/C++、Java项目做准备;环境:Ubuntu:20.04cmake:3.16.3简单尝试:用C++写......
  • Rust学习笔记——基础篇3:数据类型
    数据类型整数类型位长度有符号无符号8-biti8u816-biti16u1632-biti32u3264-biti64u64128-biti128u128archisizeusize整数型的表述方式进制例十进制98_222十六进制0xff八进制0o77二进制0b1111_0000字节(只能......