zoukankan      html  css  js  c++  java
  • 020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步

    多线程

    一、线程概述

    1. 进程

    正在执行的应用程序(java.exe),一个可执行的程序一次运行的过程

    • 独立性:不同进程之间相互独立
    • 动态性:是一直活动的
    • 并发性:多个进程可以在单个处理器上同时运行

    2. 线程

    线程是程序中的一个执行流,每个线程都有自己的专有寄存器,但代码区是共享的,即不同的线程可以执行同样的函数

    • 各线程在运行过程中可能会出现资源竞争
    • CPU在不同的线程之间切换
    • 每个线程的执行机会相对随机

    3. 进程与线程

    • 一个线程只能属于一个进程
    • 一个进程可以有多个线程,至少有一个主线程
    • 同一进程的所有线程共享系统分配给该进程的资源
    • 线程是指进程内的一个执行单元,也是进程内的可调度实体
    • 系统创建进程时需要重新分配资源,而创建线程相对容易,因此效率更高

    4. 多线程

    多线程是指程序中包含多个执行流,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务

    5. 主线程

    任何一个Java程序启动时,一个线程立刻运行,执行main方法(程序的入口),这个线程称为程序的主线程

    • 主线程是产生其它子线程的线程
    • 通常必须最后关闭,因为它要执行其它子线程的关闭工作

    二、多线程优缺点

    (1)优点

    • Java支持编写多线程程序
    • 可以并发执行多个任务
    • 最大限度的减少CPU的闲置时间,提高CPU的利用率

    (2)缺点

    • 线程越多占用内存越多
    • 线程之间对共享资源访问会互相影响
    • 线程过多会导致控制复杂化

    三、线程的创建

    Thread类也实现了Runnable接口

    1. 继承Thread类

    Thread类中的run方法本身并不执行任何操作,需要继承Thread类后重写该方法

    • MyThread
    // TODO 线程实现实现方式:继承Thread类
    		System.out.println(111);
    		// 1.声明一个定义的线程对象 - 父类引用指向子类对象
    		Thread thread1 = new MyThread();
    		thread1.setName("子线程1");
    		// 2.使一个线程进入到就绪状态:start()方法
    		// 线程启动后会调用相应的run方法
    		thread1.start();
    		// 直接调用run方法相当于执行一个普通类下的普通方法
    		// 此时程序会等待方法调用完成后按顺序执行
    		 thread1.run();
    		System.out.println(222);
    		// 多个子线程在执行时,执行的机会相对随机
    		Thread thread2 = new MyThread("子线程2");
    		thread2.start();
    		 thread2.run();
    		System.out.println(333);
    
    • ThreadTest
    // 实现线程的方式 - 继承Thread类
    public class MyThread extends Thread {
    	
    	// 显示声明空的构造方法
    	public MyThread() {
    		
    	}
    	
    	// 子类中调用父类的构造方法
    	public MyThread(String name) {
    		// 在线程实例化时指定线程名称
    		super(name);
    	}
    	
    	// 重写run方法
    	public void run() {
    		for (int i = 0; i < 10; i++) {
    			// Thread.currentThread()获得当前所在线程的对象
    			if (i == 5) {
    				if (Thread.currentThread().getName().equals("子线程1")) {
    					// 当某一个线程中出现异常时,不会影响另外一个线程
    					System.out.println(1/0);
    				}
    			}
    			System.out.println(Thread.currentThread() + ":" + i);
    		}
    	}
    
    }
    
    • 小练习
    // TODO 多线程练习:相对独立的线程 - 继承Thread
    		// 某个商城进行促销活动,商城内存在多个商家
    		// -》类比思想:商场开门 - 主线程启动
    		System.out.println("商场开门");
    		// 每个商家售卖不同的商品,并有不同的库存
    		Thread thread1 = new ShopThread("夏季T恤", 35, "李宁品牌服装店");
    		Thread thread2 = new ShopThread("洁面神器", 50, "韩国护肤品牌店");
    		Thread thread3 = new ShopThread("kindle", 30, "亚马逊直销店");
    		// -》封装实体:能够刻画商家售卖商品以及表示库存
    		// 描述该商城当天开门直到所有商家商品售空的过程
    		thread1.start();
    		thread2.start();
    		thread3.start();
    		thread1.join();
    		thread2.join();
    		thread3.join();
    		// -》类比思想:商品售空,库存为0时线程正常停止
    		// 使用两种方式实现
    		System.out.println("商场关门");
    
    // 多家店铺同时售卖商品
    public class ShopThread extends Thread {
    
    	public ShopThread() {
    
    	}
    
    	public ShopThread(String productName, Integer count, String name) {
    		super(name);
    		this.count = count;
    		this.productName = productName;
    	}
    
    	// 售卖的商品
    	private String productName;
    	// 商品的库存
    	private Integer count;
    
    	public String getProductName() {
    		return productName;
    	}
    
    	public void setProductName(String productName) {
    		this.productName = productName;
    	}
    
    	public Integer getCount() {
    		return count;
    	}
    
    	public void setCount(Integer count) {
    		this.count = count;
    	}
    
    	// 使用相对随机的方式减少商品的库存
    	public void run() {
    		for (int i = count; i > 0;) {
    			if ((--i) != 0) {
    				// 通过线程的name属性刻画商家的名称
    				System.out.println(Thread.currentThread().getName() + "卖出一件" + this.productName + ",仅剩" + i + "件");
    			} else {
    				System.out.println(Thread.currentThread().getName() + "商品已售完,关店");
    			}
    		}
    	}
    
    }
    

    2. 实现Runnable接口

    • MyRunnable
    // 自定义类实现Runnable接口
    public class MyRunnable implements Runnable {
        // 重写run方法
    	@Override
    	public void run() {
    		for (int i = 0; i < 10; i++) {
        		// 获得当前线程的名称
    			System.out.println(Thread.currentThread().getName() + ":" + i);
    		}
    	}
    }
    
    • RunnableTest
    public class RunnableTest {
    
    	public static void main(String[] args) {
            // 将一个实现了Runnalbe接口的实现类的实例传入
    		MyRunnable myRunnable1 = new MyRunnable();
    		MyRunnable myRunnable2 = new MyRunnable();
    		// 调用相应的构造方法,指定线程名
    		Thread thread1 = new Thread(myRunnable1, "线程1");
            // 启动线程
    		thread1.start();
            // 调用相应的构造方法,指定线程名		
    		Thread thread2 = new Thread(myRunnable2, "线程2");
    		// 启动线程
    		thread2.start();
    	}
    }
    
    • 小练习
    // TODO 多线程练习:相对独立的线程 - 实现Runnable
    		System.out.println("商场开门");
    		// 实例化Runnable接口的对象
    		Runnable runnable1 = new ShopRunnable("夏季T恤", 350);
    		Runnable runnable2 = new ShopRunnable("洁面神器", 500);
    		Runnable runnable3 = new ShopRunnable("kindle", 300);
    		// 通过线程控制目标对象
    		Thread thread1 = new Thread(runnable1, "李宁品牌服装店");
    		Thread thread2 = new Thread(runnable2, "韩国护肤品牌店");
    		Thread thread3 = new Thread(runnable3, "亚马逊直销店");
    		// 线程启动
    		thread1.start();
    		thread2.start();
    		thread3.start();
    		// 可以设置最长等待时长
    		// 超过等待时长,继续运行当先线程内容
    		// 不指定时长时,等待线程完全执行完毕再执行当前线程
    		thread1.join(10);
    		thread2.join(10);
    		thread3.join(10);
    		System.out.println("商场关门");
    
    // 多家店铺同时售卖商品
    public class ShopRunnable implements Runnable {
    
    	public ShopRunnable() {
    		
    	}
    
    	public ShopRunnable(String productName, Integer count) {
    		this.count = count;
    		this.productName = productName;
    	}
    
    	// 售卖的商品
    	private String productName;
    	// 商品的库存
    	private Integer count;
    
    	public String getProductName() {
    		return productName;
    	}
    
    	public void setProductName(String productName) {
    		this.productName = productName;
    	}
    
    	public Integer getCount() {
    		return count;
    	}
    
    	public void setCount(Integer count) {
    		this.count = count;
    	}
    	
    	// 使用相对随机的方式减少商品的库存
    	public void run() {
    		for (int i = count; i > 0;) {
    			if ((--i) != 0) {
    				// 通过线程的name属性刻画商家的名称
    				System.out.println(Thread.currentThread().getName() + "卖出一件" + this.productName + ",仅剩" + i + "件");
    			}else {
    				System.out.println(Thread.currentThread().getName() + "商品已售完,关店");
    			}
    		}
    	}
    
    }
    

    3. 内部类方式创建

    • ThreadInner
    public class ThreadInner {
    
    	public static void main(String[] args) {
    		// 使用匿名内部类实现线程执行具体内容
    		new Thread("新线程") {
    			
    			@Override
    			public void run() {
    				for (int i = 0; i < 10; i++) {
    					System.out.println(this.getName() + ":" + i);
    				}
    			}
    		// 启动线程
    		}.start();
    	}
    }
    
    • RunnableInner
    public class RunnableInner {
    	
    	public static void main(String[] args) {
    		// 实例化线程
    		new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				for (int i = 0; i < 10; i++) {
    					System.out.println(Thread.currentThread().getName() + ":" + i);
    				}
    			}
    		// 启动线程
    		}, "新线程").start();
    	}
    }
    

    四、线程常用方法

    1. 设置线程名称

    • setName(String name)
    • Thread(String name)

    2. 设置线程休眠

    • sleep(long millis):进入阻塞状态,时间单位为毫秒

    3. 设置线程优先级

    默认情况下线程的优先级与创建它的父线程具有相同的优先级,提升抢到时间片的概率,从而获得更多的执行机会

    • setPriority(int newPriority):参数范围为1-10,需要在执行start()方法前设置

    4. 等待某一线程终止

    • join():先执行start()方法,再执行join方法,当前线程执行完毕后,会继续执行其它线程

    5. 后台线程

    也称为守护线程或用户线程,如JVM的垃圾回收线程,必须在start()前执行,如果所有的前台线程都死亡,则会自动死亡

    • setDaemon(boolean on)

    五、生命周期

    线程启动后进入到就绪状态,多个线程在同时运行时,不断的在争抢CPU的时间片

    • 新生:实例化完成
    • 就绪:准备运行,暂时没有分配到时间片
    • 运行:分配到时间片,执行相关任务
    • 阻塞:由于某些特殊情况导致阻塞,会在合适的机会重新进入就绪状态
    • 死亡:线程结束(可以使用stop()方法强制结束)

    六、多线程同步

    把竞争访问的资源标识为private
    使用synchronized(同步的)关键字同步方法或代码块

    • Ticket
    public class Ticket implements Runnable {
    
    	// 使用private定义竞争资源
    	private int ticket = 100;
    
    	@Override
    	public void run() {
    		while (true) {
    			// 对当前线程进行同步加锁
    			synchronized (this) {
    				ticket--;
    				// 当车票售空时跳出循环
    				if (ticket < 0) {
    					break;
    				}
    				System.out.println(Thread.currentThread().getName() + "购买,当前剩余票数:" + ticket);
    			}
    		}
    	}
    }
    
    • TicketTest
    public class TicketTest {
        // 模拟两名旅客的抢票
    	public static void main(String[] args) {
    		Ticket ticket = new Ticket();
    		Thread thread1 = new Thread(ticket,"旅客1");
    		Thread thread2 = new Thread(ticket,"旅客2");
    		thread1.start();
    		thread2.start();
    	}
    }
    
  • 相关阅读:
    寻找两个有序数组的中位数
    JAVA设计模式(组合模式)
    excel 操作
    研究生英语-春
    cvs
    Spring课程安排
    Spring的事务管理
    在WEB项目中集成Spring
    计算机网络参考模型
    揭开5G神秘面纱
  • 原文地址:https://www.cnblogs.com/yokii/p/9445151.html
Copyright © 2011-2022 走看看