zoukankan      html  css  js  c++  java
  • java之初学线程

    线程

    学习线程相关的笔记,前面写过关于很多线程的使用,有兴趣的可以去了解下

    线程

    概念理解

    • 并发 : 指两个或多个事件在同一个时间段内发生(交替执行)。
    • 并行 : 指两个或多个事件在同一时刻发生(同时发生)。
    • 进程 : 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多
      个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创
      建、运行到消亡的过程。
    • 线程 : 进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当
      于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务(一个程序至少有一个进程,一个进程可以有多个线程)。

    线程和进程的区别 :

    1.进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
    2.线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

    多线程的原理

    ![线程的原理

    ](https://images2018.cnblogs.com/blog/1224549/201807/1224549-20180717193220192-572225937.jpg)

    从图中可以看出,线程的启动实际上是开辟了新的空间,这样的话jvm就可以在U盾讴歌栈之间相互切换,这个就是多线程的原理.

    Runnable接口

    Runnable是多线程的祖宗类,多线程都间接或直接的实现了这个接口.内部只有run方法,所以也可以看出run方法时多线程的核心.

    Thread类

    先了解一下Thread类的内部方法:

    构造方法:

    • public Thread() :分配一个新的线程对象。
    • public Thread(String name) :分配一个指定名字的新的线程对象。
    • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
    • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

    常用方法:

    • public String getName() :获取当前线程名称。
    • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
    • public void run() :此线程要执行的任务在此处定义代码。
    • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

    使用Thread来创建线程

    1. 创建Thread类的子类
    2. 在Thread类的子类中重写run方法,设置线程任务
    3. 创建Thread类的子类
    4. 调用Thread类的方法start方法,开启新的线程,执行run方法.(这需要注意,是调用start方法,而不是直接调用run方法)

    void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。结果是两个线程并发执行,当前线程(main)
    和另一个线程(创建的新线程,执行其run方法),多次启动一个线程是非法的.特别当线程已经结束后,不能重新启动.java程序
    是抢占式调度,哪个线程优先级高,哪个线程先执行;同一个优先级,随机选择一个.

    获取线程名称

    获取线程的名称有两种方法:

    • 使用getName()获取线程名称,这个需要使用Thread类的子类对象来调用.
    • 使用Thread.currentThread().getName()获取线程名称(推荐使用)
    设置线程名称

    设置线程的名称也有两种方式:

    • 使用setName()方法设置线程名称,也是需要Thread类或者子类的对象调用.
    • 使用构造方法,可以使用有参构造来设置线程名称.

    Runnable接口

    1. 定义一个类,实现Runnable接口(任务类).
    2. 重写Runnable的run方法
    3. 创建任务类对象
    4. 创建线程类对象,并将任务对象参数作为参数进行传递
    5. 使用线程类对象调用start方法启动线程.

    这个过程中需要注意的是第四步,需要使用public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
    public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。这两个构造方法.

    创建线程的方式

    • 实现Runnable接口

        /**
         * 使用实现Runnable接口实现多线程
         *
         * @author WZLOVE
         * @create 2018-07-16 19:46
         */
        public class MyRunnable implements Runnable{
        
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }
      
    • 继承Thread类

        /**
         * 使用继承实现多线程
         *
         * @author WZLOVE
         * @create 2018-07-16 19:47
         */
        public class MyThread extends Thread{
        
            public MyThread() {
            }
        
            public MyThread(String name) {
                super(name);
            }
        
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(getName() + ":" + i);
                }
            }
        }
      
    • 不同的方法实现多线程,创建对象使用的方式也不同:

        /**
         * @author WZLOVE
         * @create 2018-07-16 19:45
         */
        public class ThreadDemo {
        
            public static void main(String[] args) {
                // 继承创建对象相对简单
                MyThread mt = new MyThread("继承");
                mt.start();
        
                // 实现接口创建对象
                // 创建自定义类对象
                MyRunnable mr = new MyRunnable();
                // 创建线程对象
                Thread thread = new Thread(mr,"实现接口");
                thread.start();
            }
        }
      

    注意点:

    1.实际上,Thread类也实现了Runnable接口.
    2.所有的多线程代码都在run方法里面
    3.所有的多线程代码都是通过运行Thread的start()方法来运行的
    4.Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。
    而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。

    实现Runnable接口与继承Thread类的不同点

    使用接口比类是更有优势的:

    • 可以避免java中的单继承的局限性。
    • 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立(任务类和线程类进行分离,解耦)。
    • 但是实现接口不能直接使用Thread类的方法,但是可以通过获取当前线程对象进行调用方法.
    • 适合多个相同的程序代码的线程去共享同一个资源。
    • 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

    线程的安全问题的产生

    线程的安全问题的产生是由于多个线程对共享资源的访问.简单的说线程安全问题都是由全局变量及静态变量引起的。
    若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;
    若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
    买电影票的例子(出现安全问题,运行一下):

    package com.wzlove.thread.movie;
    
    /**
     * 票数的任务类
     *
     * @author WZLOVE
     * @create 2018-07-17 14:35
     */
    public class TicketRunnableImpl implements Runnable{
        /**
         * 定义共享资源
         */
        private  int ticket = 100;
        @Override
        public void run() {
            while  (true){
                if(ticket > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+  ticket -- + "张票");
                }
    
            }
        }
    }
    
    
    
    package com.wzlove.thread.movie;
    
    /**
     * 多线程安全问题的测试
     *
     * @author WZLOVE
     * @create 2018-07-17 14:37
     */
    public class MovieDemo {
    
        public static void main(String[] args) {
            // 创建任务类对象
            TicketRunnableImpl ticketRunnable = new TicketRunnableImpl();
            // 创建线程对象
            Thread t1 = new Thread(ticketRunnable,"窗口1");
            Thread t2 = new Thread(ticketRunnable,"窗口2");
            Thread t3 = new Thread(ticketRunnable,"窗口3");
            // 开始线程
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

    上面的情况就会出现线程安全问题.

    线程的安全问题的解决

    使用同步解决线程的安全问题.

    • 同步代码块
    • 同步方法
    • lock锁机制

    1.同步代码块

    格式 :

        // 同步对象可一是任意对象,推荐使用this
        synchronized(同步对象){
            可能出现同步问题的代码
        }
    

    锁对象可以是任意对象,但是锁对象必须唯一.

    2.同步方法

    格式:

        // 同步方法也是有锁对象的,也就是当前对象this
        修饰符 synchronized 返回值类型 方法名(参数列表){
            可能出现同步问题的代码
        }
    

    需要注意的是静态同步方法内的同步锁不是this(因为静态代码的执行在this之前产生),而是类的字节码对象,也就是(类名.class)

    3.Lock锁机制

    • public void lock() :加同步锁。
    • public void unlock() :释放同步锁。

    使用步骤:

    1. 创建ReentrantLock的对象
    2. 在使用共享资源前使用lock方法进行加锁
    3. 在使用共享资源结束后使用unlock方法释放同步锁

    线程状态的概述

    线程状态 导致状态发生条件
    new()新建 线程刚被创建,但是并未启动。还没调用start方法。
    Runnable(可运行) 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操 作系统处理器。
    Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状 态;当该线程持有锁时,该线程将变成Runnable状态。
    Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个 状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
    Timed Waiting(计时等待) 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态 将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、 Object.wait。
    Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

    状态图:
    状态图

    等待唤醒机制

    Object类中的四个方法:

    • wait() : 使线程处于等待状态,等待着其他的线程唤醒
    • wait(long millis) : 使线程处于等待状态, 等待着其他的线程唤醒/等待时间到达
    • notify() : 唤醒其他的单个等待的线程
    • notifyAll() : 唤醒其他的所有等待的线程

    sleep()与wait()

    • sleep:Thread类中的静态方法,休眠指定的时间,在指定时间后自动唤醒,不回释放锁对象
    • wait : Object类中的方法,无限等待,等待其他线程的唤醒,必须释放锁对象
  • 相关阅读:
    2019-2020-3 20175324 王陈峤宇《网络对抗技术》Exp3 免杀原理与实践
    2019-2020-2 20175324 王陈峤宇《网络对抗技术》Exp2 后门原理与实践
    2019-2020-3 20175324王陈峤宇《网络对抗技术》Exp1 PC平台逆向破解
    2019-2020- 20175324王陈峤宇 Exp0 环境搭建-KALI LINUX安装
    2019-2020-2 20175330杨璟旭《网络对抗技术》Exp9 Web安全基础
    2019-2020-2 网络对抗技术 20175330 Exp8 Web基础
    2019-2020-2 网络对抗技术 20175330 Exp7 网络欺诈防范
    2019-2020-2 网络对抗技术 20175330杨璟旭 Exp6 MSF基础应用
    2019-2020-2 20175330杨璟旭《网络对抗技术》Exp5信息搜集与漏洞扫描
    2019-2020-2 20175330杨璟旭《网络对抗技术》Exp4 恶意代码分析
  • 原文地址:https://www.cnblogs.com/wadmwz/p/9325466.html
Copyright © 2011-2022 走看看