多线程介绍和操作

多线程简介

1.1并发和并行
  • 并行:指两个或多个事件在同一时刻发生(同时发生)。多核CPU的基础上
  • 并发:指两个或多个事件在同一个时间段内发生。单核CPU的基础上

在操作系统上,单CPU系统中,每一时刻只能运行一个程序,宏观是多个程序同时运行,微观是分时交替进行,是因为分时交替运行的时间非常短。
多核处理器可以每个处理器并发执行程序,这样多个程序可以同时执行,提高电脑运行效率。

单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也一样的,从宏观角度上理解线程是并行运行的,但是从微观上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会已某种顺序执行多个线程,我们把这种情况称之为线程调度。

1.2线程和进程
  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程
  • 线程:进程内部的一个独立执行单元;一个进程可以同时并发的运行你多个线程,可以理解为一个进程便相当于一个单CPU操作系统,而线程便是这个系统中运行的多个任务
    线程与进程的区别:
  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程
  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
    注意:
  1. 因为一个进程中的多个线程是并发运行的,从微观上看也是有先后顺序的,哪个线程执行完全取决于CPU的调度,程序员干涉不了。这就造成了多线程的随机性。
  2. java程序的进程里至少包含两个线程,主进程也就是main()方法线程,另外一个是垃圾回收机制线程。每当使用java命令执行一个类时,实际都会启动一个JVM,每个JVM实际上就是在操作系统中启动了一个线程,java本身具备了垃圾的收集机制,所以在java运行时至少会启动两个线程。
  3. 由于创建一个线程的开销比创建一个进程小的多,我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
    线程调度:
    计算机通常只有一个CPU时,在任意时刻只能执行一条计算机指令,每个进程只有获得CPU的使用权才能执行指令。所谓多进程并发运行,从宏观上其实是各个进程轮流获得CPU的使用权,分别执行各自任务。那么在可运行池中,会有多个线程处于就绪状态等到CPU,JVM就负责了线程的调度。JVM采用的事抢占式调度,没有采用分时调度,因此可以造成多线程执行结果的随机性。

多线程实现的两种方式

1.1继承Thread类

构造方法:

  • public Thread():分配一个新的线程对象
  • public Thread(String name):分配一个指定名字的新的线程对象。
    常用方法:
  • public String getName():获取当前线程的名称。
  • setName 设置新的名字
  • public void start():导致此线程开始执行;java虚拟机调用此线程的run方法。
  • public void run():此线程要执行的任务在此处定义代码。
  • public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂停停止执行)
  • public static Thread currentThread():返回对当前正在执行的线程对象的引用。
class A extends Thread{
	public void run(){  
	// 重写Thread类的run方法,run方法是编写线程任务的地方
		for(int i=1;i<=30;i++){
			System.out.println("A..."+i);
			
		}
	}
}
public class Test2 {
	public static void main(String[] args) {
		A a = new A();	
	/*	a.run();  // 方法调用,不能实现并发的效果
		*/
		a.start();  // 启动线程A  --> 线程启动后,自动会调用run方法
	}
}

main也是线程(主线程)

1.2实现Runnable接口

只需要重写run方法即可

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
  2. 创建Runnable实现类的案列,并以此案例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
  • public Thread(Runanble target):分配一个带有指定目标新的线程对象
  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
  1. 调用线程对象的start()方法来启动线程。
public class MyRunnable implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}
}
public class Demo {
    public static void main(String[] args) {
        //创建自定义类对象  线程任务对象
        MyRunnable mr = new MyRunnable();
        //创建线程对象
        Thread t = new Thread(mr, "小强");
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("旺财 " + i);
        }
    }
}

通过实现Runnable接口,使得该类有了多线程的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run()方法里面。Thread类实际也是实现了Runnable接口的类。
在启动的多线程的时候,需要通过Thread类的构造方法Thread(Runnable target)构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

1.3Thread和Runnable的区别

一个类继承Thread则不适合资源共享。如果是实现Runnable接口的话,很容易实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 线程池只能放入实现Runnable或callable类线程,不能直接放入继承Thread类。

扩充:在java中,每次程序运行至少启动两个线程。一个是main线程买一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实就是在操作系统启动了一个进程。

1.4匿名内部类方式实现线程的创建

使用线程的内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重写Runnable接口中的run方法:

public class NoNameInnerClassThread {
   	public static void main(String[] args) {	   	

        Runnable r = new Runnable(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                  	System.out.println("张宇:"+i);
                }
            }  
        };
        new Thread(r).start();

        for (int i = 0; i < 20; i++) {
          	System.out.println("费玉清:"+i);
        }
   	}
}

线程的生命周期

1.1生命周期概述

线程是一个动态的概念,有创建的时候,也有运行和变化的时候,也就有消亡的时候,所以从生到死就是一个生命周期。在生命周期中,有各种各样的状态,这些状态可以相互转换。

  • 新建态:刚创建好对象的时候,刚new出来的时候。
  • 就绪态:线程准备好了所有运行的资源,只差CPU来临。
  • 运行态:CPU正在执行的线程的状态
  • 阻塞态:线程线程主动休息、或者缺少一些运行的资源,及时CPU来临,也无法运行。
  • 死亡态:线程运行完成、出现异常。调用方法结束
  • 在这里插入图片描述
    在这里插入图片描述
1.2java中关于线程状态的描述

java语言中,获取线程状态的方法:
getState():返回当前线程对象的状态对象;
说明:state的六种状态

  • NEW:新建态,没有开启线程
  • RUNNABLE:就绪态和运行态
  • BLOCKED:阻塞态(等待锁、I\O)
  • WAITING:阻塞态(调用了wait方法,等待其他线程唤醒的状态)
  • TIMED_WAITING:阻塞态(调用了有时间限制的wait方法、sleep方法)
  • TERMINATED:死亡态
1.3主线程

在任何java程序启动时,一个线程立刻运行(即main方法对应的线程),该线程通常称为程序 的主线程。
主线程的特点:
它是产生其他子线程的线程。
它不一定是最后完成执行的线程,子线程可能在它结束之后还在运行。

class A extends Thread{

    public void run() {
        for(int i=1;i<=20;i++){
            System.out.println(Thread.currentThread().getName()+"..."+i);
            try {
                sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

class B extends Thread{
    public void run() {
        for(int i=1;i<=20;i++){
            System.out.println(Thread.currentThread().getName()+"<<<"+i);
            try {
                sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

public class Test2 {
    public static void main(String[] args) {
        new A().start();
        new B().start();
        for(int i=1;i<=20;i++){			
            System.out.println(Thread.currentThread().getName()+"<<<"+i);			
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }	
    }
}

多线程相关操作

1.1停止线程的方式
  • 线程stop方法(过时)
  • 定义标识的方法
public static boolean flag = false;  //定义一个标识
1.2线程的优先级

每个线程都有优先级,优先级高的获得较多的执行机会。
只有在系统资源紧张的时候设置优先级才有意义,否则执行时机是一样的。
但是并不是有高优先级的线程执行时,低优先级的线程就不能执行。
Java中,线程优先级分为10级

  • Thread.MAX_PRIORITY = 10
  • Thread.NORM_PRIORITY = 5
  • Thread.MIN_PRIORITY = 1
    getPriority():返回当前线程对象的优先级,默认是5级
    setPriority(int newPriority):设置线程的优先级,虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大10,最小1,默认5)
Thread.currentThread().setPriority(10);  // 设置线程优先级
System.out.println(Thread.currentThread().getPriority()); //获取线程优先级

多线程相关操作

1.1线程的join方法

非静态方法 ,当前线程挂起(阻塞),等待加入(join)的线程执行完成。
当前线程用t.join()意味着,当前线程挂起直到目标线程t结束。
写在哪个线程里面哪个线程就会挂起,暂停执行,等到目标线程执行完毕,在执行当前线程。

if(i==10){
    t1.start();
    t1.join(); 
    // 当主线程的i==10时,t1线程加入了
    // 主线程挂起(暂停), 当t1线程运行结束后,主线程继续
1.2线程的yield方法

和sleep方法很像,但是和sleep不同,它们只是短暂的挂起当前线程,让别的线程先运行,进入到就绪状态。
。。。。

1.3守护线程(后台线程)

当所有线程完成时,java程序退出,这句话并不准确,因为gc这种垃圾回收线程,我们无法停止,但是java程序在所有非守护线程完成后退出,守护线程只有在别的线程运行的时候才会有意义。
后台线程的特征:当所有前台线程都运行完成后,无论后台线程是否运行完成都要结束。(是一段时间内结束,并不会理解结束)
实现:setDaemon(boolean on)
必须在启动线程之前(调用start方法之前)调用setDaemon(true)方法,才可以把该线程设置为后台线程。

Thread t1 = new Thread(new AA());
	Thread t2 = new Thread(new BB());
	t1.setDaemon(true);  
	// 把t1设置成守护线程(后台线程)
    // 当t2结束后,t1也结束
	t1.start();
	t2.start();

多线程相关操作

1.1线程安全

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话可能影响线程安全。

1.2线程同步

当我们使用多个线程访问统一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题
要解决多线程并发访问多个资源的安全性问题,java提供了同步机制(synchronized)来解决。
三种方式完成同步操作:

  1. 同步代码块
  2. 同步方法
  3. 锁机制
1.3同步代码块
  • **同步代码块:**synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
  • 格式:
synchronized(同步锁){
需要同步操作的代码}
  • 同步锁:
    对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁。
  1. 锁对象可以是任意类型
  2. 多个线程对象要使用同一把锁

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着。

1.4同步方法
  • 同步方法:
    使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
  • 格式:
public synchronized void method(){
	可能产生线程安全问题的代码
}
1.5lock锁
  1. java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有功能Lock都有,除此之外更强大,更体现面向对象。
  2. Lock锁也称同步锁,加锁与释放锁方法化了
    -public void lock():加同步锁
    public void unlock():释放同步锁
1.6死锁
  • 多个线程竞争多个排它锁的时候,可能出现死锁
  • 互相等待对方资源成为死锁
  • 最典型的死锁:线程1持有ObjectA上的锁,并等待ObjectB上的锁,线程2持有ObjectB上的锁,并等待OBjectA上的锁,并一直等待第二个锁,因此会永远等下去。
public class Demo15 {
    public static void main(String[] args) {
        X x = new X();
        Y y = new Y();
        x.start();
        y.start();

    }
}
class X extends Thread{
    public void run(){

        synchronized ("aaa") {

            System.out.println("线程X锁定aaa,并等待锁定bbb");
            synchronized ("bbb") {
            }	
        }		
    }
}

class Y extends Thread{

    public void run(){

        synchronized ("bbb"){
            System.out.println("线程Y锁定bbb,并等待锁定aaa");

            synchronized ("aaa") {
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_不吃香菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值