Java之多线程

1.多线程的引入

多线程的引入

  1. 如果程序只有一条执行路径,那么该程序就是单线程。
  2. 如果程序有多条执行路径,那么该程序就是多线程。
2.进程及线程的概述
  1. 要了解多线程,必须先了解线程。要想了解线程,必须先了解进程,因为线程是依赖进程而存在。
  2. 什么是进程?
    • 进程:就是正在运行的程序。
    • 进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
  3. 多进程有什么意义呢?
    • 单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
    • 举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
    • 现在的计算机都是支持多进程的,就可以在一个时间段内执行多个任务。可以提高CPU的使用率。
    • 一边玩游戏,一边听音乐是同时进行的吗?不是,因为单CPU在某一个时间点上只能做一件事情。而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
  4. 什么是线程?
    • 在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
    • 线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
    • 单线程:如果程序只有一条执行路径。
    • 多线程:如果程序有多条执行路径。
  5. 多线程有什么意义呢?
    • 多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
    • 程序的执行其实都是在抢CPU的资源,CPU的执行权。
    • 多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
    • 我们不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
3.多线程举例及并发和并行的区别
  1. 多线程举例:扫雷程序,迅雷下载。
  2. 并行和并发的区别?
  • 并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。
  • 并发:物理上同时发生,指在某一个时间点同时运行多个程序。
4.实现多线程Thread
  1. 如何实现?
  • 由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
  • 而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
  • Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
  • 但是,Java可以调用C/C++写好的程序来实现多线程程序。
  • 由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。这样就可以实现多线程程序了。
  1. 那么Java提供的类是什么呢?
  • Thread
  • 有两种方式实现多线程程序。
  1. 方式1:继承Thread类
    1. 自定义类MyThread继承Thread类。
    2. MyThread类里面重写run()?
      • 不是类中的所有代码都需要被线程执行。
      • 为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含哪些被线程执行的代码。
    3. 创建对象。
    4. 启动线程。
  2. 面试题:run()和start()的区别?
  • run():仅仅是封装被线程执行的代码,直接调用时普通方法。
  • start():首先启动了线程,然后再由JVM去调用该线程的run()方法。
  1. IllegalThreadStateException:非法线程状态异常。相当于某线程被调用了两次,而不是两个线程启动。
5.Java程序运行原理和JVM的启动是多线程的
  1. java程序的运行原理
  • 有java命令启动JVM,JVM启动就相当于启动了一个进程。接着由该进程创建了一个主线程去调用main方法。
  1. JVM虚拟机的启动是多线程的。
  • 原因:垃圾回收线程也要先启动,否则很容易出现内存溢出。
  • 现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以JVM的启动是多线程的。
6.获取和设置线程对象名称
  1. 如何获取线程对象的名称?
  • public final String getName();
  1. 名称为什么是:Thread-? 编号
class Thread{
    private char name[];

    public Thread(){
        init(null,null,"Thread-"+nextThreadNum(), 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name, long stackSize){
        init(g, target, name, stackSize, null);
    }
    
    private void init(ThreadGroup g, Runnable target, String name, long stackSize,AccessControlContext acc){
        this.name = name.toCharArray();
    }
    
    public final void setName(String name){
        this.name = name.toCharArray();
    }
    
    private static int threadInitNumber;//return 0,1,2
    private static synchronized int nextThreadNum(){
        return threadInitNumber++;//return 0,1
    }
    
    public final String getName(){
        return String.valueOf(name);
    }
}

class MyThread extends Thread{
    public MyThread(){
        super();
    }
}
  1. 如何设置线程对象的名称?
  • public final void setName(String name):设置线程对象的名称
  1. 获取main方法所在的线程对象的名称,该怎么办?(针对不是Thread类的子类中如何获取线程对象名称呢?)
  • Thread类提供了一个方法
  • public static Thread currentThread();返回当前正在执行的线程对象
  • Thread.currentThread().getName();
7.线程调度及获取和设置线程的优先级
  1. 获取线程对象的优先级?
  • public final int getPriority();返回线程对象的优先级
  1. 设置线程对象的优先级?
  • public final void setPriority(int newPriority); 更改线程的优先级
  • 注意:
    1. 线程默认优先级是5。
    2. 线程优先级的范围是:1-10.
    3. 线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
  1. IllegalArgumentException:非法参数异常:抛出的异常表明向方法传递了一个不合法或不正确的参数。
8.线程控制之…
  1. 线程休眠:public static void sleep(long millis);
  2. 线程加入:public final void join(); 等待该线程终止
  3. 线程礼让:public static void yield(); 暂停当前正在执行的线程对象,并执行其他线程。
  4. 守护线程(后台线程):
  • public final void setDaemon(boolean on); 将该线程标记为守护线程或用户线程。
  • 当正在运行的线程都是守护线程是,Java虚拟机退出,该方法必须在启动线程前调用。 举例:坦克大战。
  1. 中断线程:
  • public final void stop(); 让线程停止,过时了,但是还可以使用。
  • public void interrupt(); 中断线程,把线程的状态终止,并抛出一个InterruptedException
9.线程生命周期图解(面试题)
  1. 面试题:线程的生命周期?
  • 新建:创建线程对象。
  • 就绪:有执行资格,没有执行权。
  • 运行:有执行资格,有执行权。
    • 阻塞:由于一些操作让线程处于了该状态。没有执行资格,没有执行权,而另一些操作却可以把它激活,激活后处于就绪状态。
  • 死亡:线程对象变成垃圾,等待被回收。
  1. 图解
    线程生命周期图解
10.多线程两种方式的图解比较及区别
  1. 方式一:继承Thread类
  • 自定义类MyThread继承Thread类
  • 在MyThread类中重写run()
  • 创建MyThread类的对象
  • 启动线程对象
  1. 方式二:实现Runnable接口
  • 自定义类MyRunnable实现Runnable接口
  • 在MyThread里面重写run()
  • 创建MyRunnable类的对象
  • 创建Thread类的对象,并把上一步骤的对象作为构造参数传递
  1. 为什么要Runnable接口的实现方式
  • 可以避免由于Java单继承带来的局限性。
  • 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效的分离,较好的体现了面向对象的设计思想。
11.线程安全问题的产生原因分析
  1. 判断一个程序是否会有线程安全问题的标准
  • 是否是多线程环境
  • 是否有共享数据
  • 是否有多条语句操作共享数据
  1. 怎么解决?
  • 思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的的时候别人不能来执行。Java给我们提供了:同步机制。
  • 同步代码块:
synchronized(对象){
    需要同步的代码;
}
A:对象是什么呢?
- 随便创建一个试一试
B:需要同步的代码是哪些呢?
- 把多条语句操作共享数据的代码的部分给包起来。
注意:同步解决安全问题的根本原因在那个对象上,该对象如同锁的功能。(多线程必须是同一把锁)
  1. 同步的特点:
  • 前提:多个线程
  • 解决问题要注意:多个线程使用的是同一个锁对象。
  • 同步的好处:同步出现解决了多线程的安全问题。
  • 同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
  • A:同步代码块的锁对象是谁呢?
    • 任意对象
  • B:同步方法的格式及锁对象问题?
    • 把同步关键字加在方法上。
    • 同步方法是谁呢?
    • this
  • C:静态方法及锁对象问题?
    • 静态方法的锁对象是谁呢?
    • 类的字节码文件对象(反射)
12.卖票的一些问题
try {
       Thread.sleep(100);
    } catch (InterruptedException e) {
         e.printStackTrace();
   }
  • 加入延迟之后,就产生了两个问题:
    1. 相同的票卖了多次
      • CPU的一次操作必须是原子性(最简单的)的。
    2. 出现了负票数
      • 随机性和延迟导致的。
package com.wal1;

public class SellTicket implements Runnable {

	/*private int tickets = 100;
	private Object obj = new Object();
	
	private int x = 0;
	
	@Override
	public void run() {
		while(true){
			if(x % 2 == 0){
			synchronized(this){
			if(tickets > 0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
			}
			}
			}else{
				sellTicket();
			}
			x++;
		}
	}
	
	public synchronized void sellTicket(){
			if(tickets > 0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
			}
			
	}*/
	
	
	private static int tickets = 100;
	//private Object obj = new Object();
	private int x = 0;
	
	@Override
	public void run() {
		while(true){
			if(x % 2 == 0){
			synchronized(SellTicket.class){
			if(tickets > 0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
			}
			}
			}else{
				sellTicket();
			}
			x++;
		}
	}
	
	public static synchronized void sellTicket(){
			if(tickets > 0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在抢第"+tickets--+"张票");
			}
			
	}
}

package com.wal1;

public class SellTicketDemo {
	public static void main(String[] args) {
		SellTicket st = new SellTicket();
		
		Thread st1 = new Thread(st, "窗口一");
		Thread st2 = new Thread(st, "窗口二");
		Thread st3 = new Thread(st, "窗口三");
		
		st1.start();
		st2.start();
		st3.start();
	}
}

13.线程安全类回顾
  1. 线程安全的类
StringBuffer sb = new StringBuffer();
Vector<String> v = new Vector<String>();
Hashtable<String,String> h = new Hashtable<String,String>();
//Vector即使线程安全也不用你。
//那用谁呢?
//public static <T> list <T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();//线程不安全
List<String> list2 = new Collections.synchronizedList(new ArrayList<String>());//线程安全
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值