一、进程与线程的理解

线程总是和进程分不开的,但是说到线程,我们还要先介绍一下进程

1.1 对进程的理解

  • 进程:是指在系统中运行的一个应用程序,比如我们用手机打游戏的时候,有时候卡了,手机会提示是否结束应用程序,这个就是进程。电脑也是如此,比如打开一个软件的时候,长时间没有响应,鼠标点一下,电脑就会提示你是否结束应用程序,还有,在电脑上,我们按下ctrl+alt+delete 键找到任务管理器一栏,我们就可以清楚的看到电脑上正在运行中的进程,如下:

Java多线程_java

1.2 对线程的理解

  • 线程:线程就是一个正在执行的进程当中的一个子任务,所以开启一个进程(应用程序)就会进行多个子任务,共同完成任务

1.3 两者差别

两者区别:从上面的图中可以看出每个进程都会占用一定的内存,每个进程所占用的内存在操作系统中都是相互独立的,然后在线程当中,在同一块内存区域,每一个线程可以共享数据,所以线程之间的资源占用比较小

1.4 多线程

把一个线程比喻成一个子任务的话,多线程就是多个任务在同一段时间共同执行,Java中是可以编写多线程的程序的

  • 多线程的好处
    多线程的最大好处就是可以并发执行多个任务,当某一个任务因为意外终止时,那么就可以创建新的线程继续执行剩下的任务
  • 为什么用多线程?
    多线程可以极大提高 CPU 的利用率,从而提高电脑运行速度

二、Java实现多线程

2.1 两种实现方法

  • 在Java.lang.Thread类(常常用于继承该类来实现线程的操作)
  • 在Java.lang.Runnable接口(常用于实现多线程)

接下来,我们就以 泡茶 为例,给大家讲解线程的实现
首先泡茶要经过三个阶段

  1. 烧水
  2. 洗杯子
  3. 倒茶

Java多线程_应用程序_02
代码实现:

//进程之前没有 修饰符 public,不然会Java默认为一个方法
class BoilThread extends Thread {//定义烧水的线程
	
	@Override
	public void run(){//执行线程,这里用了重写的操作
		try {
			System.out.println("开始烧开水");
			Thread.sleep(10000);//延时函数,10s,基本单位是毫秒
			System.out.println("水开了!");
		} catch (Exception e) {
			// TODO: handle exception
		}	
	}
}

class WashThread extends Thread {//定义洗杯子的线程
	@Override
	public void run(){
		try {
			for(int i=1;i<=5;i++){//这里洗五个杯子
				System.out.println("开始洗第"+i+"个杯子");
				Thread.sleep(1500);
				System.out.println("第"+i+"个杯子洗完了");
			}
		} catch (Exception e) {
			// TODO: handle exception
		}	
	}	
}

public class MakeTea {
	public static void main(String[] args) {
		new BoilThread().start();//在主函数中执行线程
		new WashThread().start();
	}
}

现在大家对 线程 是不是有了更深一点的认识呢?

2.2 Java线程(Thread类)的常用用法

下面介绍一下常用的用法
线程名字可以通过 t.setName() 来设置
优先级可以通过 t.setPriority() 括号里传入1~10的整数即可
延时操作 可以通过 t.sleep() 括号传入整数,单位是毫秒
线程开始 可以同过 t.start(),结束是 t.stop() 等等

在Java 中实现线程,可以直接继承 Thread 类,然后再重写 run 方法,run方法本身不执行任何操作,但是重写之后,我们再执行,就会直接运行run 方法中的内容(详见上面的泡茶 示例)

2.2.1 Thread 的示例

Thread 类直接继承即可,在其中的方法都是静态方法,所以可以直接调用使用

public class Test {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t=Thread.currentThread();//获取当前运行的线程
		t.setName("t1");//给当前线程设置为另一个名字,否则默认为main
		System.out.println("当前的线程:"+t);
		
		try {
			for (int i = 0; i < 5; i++) {//线程的运行实例
				System.out.println("i:"+i);
				Thread.sleep(1000);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}

	}
}

Java多线程_优先级_03
运行的第一行结果如下
Java多线程_多线程_04
方括号中的第一个为我们自定义的姓名,5为这个线程的优先级(优先级是从1~10,5为默认的值),第三个是线程学名。

关于 sleep 与 yield 的解释

这两者都是 Thread 的静态方法,它们的功能都是使当前运行的线程放弃 CPU,但是还是有一些细微的差别

  • sleep 给其他线程运行机会,但不考虑其他线程的优先级,而 yield 不会把运行线程的机会留给优先级比自己低的进程
  • 线程执行了sleep 就进入了阻塞状态,执行了yield 就进入了就绪状态(操作系统里面有对进程的详细讲解)
  • 还有 sleep 在使用的时候 可能会抛出异常,所以需要 补获异常,yield 是没有这个过程的

所以一般情况还是使用 sleep 的情况比较多

2.3 Java线程(Runnable)的常用用法

在 Runnable 中只有 一个 public void run() 的抽象方法
实现 Runnable 接口并在 run() 中实现相对应的方法即可实现线程
Runnable 解决了多继承的问题

这里是把上面的 Thread 实现的代码 用 Runable重新实现了一遍

class MyThread implements Runnable{//实现接口
	@Override
	public void run() {//重写方法体
		// TODO Auto-generated method stub
		String strName=Thread.currentThread().getName();
		
		try {
			for (int i = 1; i < 11; i++) {
				System.out.println("当前线程的名字是:"+strName+" "+i);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
} 

public class ThreadTest {
	public static void main(String[] args) {
		Thread t1=new Thread(new MyThread());
		t1.setName("t1");//给线程1 设置 名称
		Thread t2=new Thread(new MyThread());
		t2.setName("t2");
		t1.setPriority(8);//给线程1设置优先级
		t2.setPriority(6);
		System.out.println("t1优先级:"+t1.getPriority());
		System.out.println("t2优先级:"+t2.getPriority());
		t1.start();//开始线程
		t2.start();
	}
}

运行结果:
Java多线程_ide_05

这里的线程执行的次数比较少,所以优先级显示的不是那么明显,改成 运行 50次就可以很快看出来啦

下面是一个实际问题,某电影院有的热映电影还剩 100张票,现在有3个窗口,要同步售票,把票给卖出去(这就是一个比较简单的多线程问题)

这里我们需要两个类来完成

public class SellTicket implements Runnable {
	private int ticketCount=100;//定义票为100张电影票
	//增加锁对象
	private Object obj = new Object(); 

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized (obj) {//使用同步锁可以避免出现负数的情况
			try {
				if(ticketCount>0){
					Thread.sleep(100);//睡眠0.1秒
					System.out.println(Thread.currentThread().getName()+"正在卖票:"+ticketCount--);
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
			}
		}
	}
}

测试类

public class TicketTest {
	public static void main(String[] args) {
		SellTicket st=new SellTicket();//实例化票的类
		Thread t1=new Thread(st, "窗口1");
		Thread t2=new Thread(st, "窗口2");
		Thread t3=new Thread(st, "窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}

这就是多线程的一个体现
Java多线程_ide_06