1.什么是进程
概念:正在运行的程序,是系统进行资源分配的基本单位。
目前的操作系统都是支持多进程的,可以同时执行多个进程,通过进程ID区分
2.什么是线程
概念:线程,又称轻量级进程,是进程中的一条执行路径,也是CPU的基本调度单位。
一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。
宏观上是并行执行,微观上是串行的(电脑是单核的,微观上是串行的;如果电脑是多核的,其实真正实现了并行执行)。
3.进程和线程的区别
- 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
- 一个程序运行后至少包含一个进程
- 一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程就没有意义。
- 进程之间不能共享数据段地址,但同进程的线程之间可以
4.线程的组成
- CPU时间片:操作系统 OS会为每个线程分配执行时间
- 运行数据:(1)堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象
(2)栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈 - 线程的逻辑代码
5.线程的特点
- 线程抢占式执行(效率高;可防止单一线程长时间独占CPU)
- 在单核CPU中,宏观上同时执行,微观上顺序执行
6、创建线程
- 继承Thread类
//创建线程的第一种方式:继承Thread类
public class MyThread extends Thread{
//1.继承Thread类
//2.重写run方法
@Override
public void run() {
for(int i=0;i<100;i++) {
//this.getId()获取线程的id this.getName()获取线程的名字(只能在继承Thread类下才可以用)
System.out.println("线程id:"+this.getId()+"线程名称:"+this.getName()+"子线程-------"+i);
//或者用下面这行代码也是一样的(不继承Thread类也可以用)【建议用这种方式,因为不继承Thread类也可以用】
//System.out.println("线程id:"+Thread.currentThread().getId()+"线程名称:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
//3.创建对象
MyThread myThread=new MyThread();
//修改线程名称
myThread.setName("我的子线程1");
//4.调用start()方法,启动线程
myThread.start();
MyThread myThread2=new MyThread();
myThread2.start();
for(int i=0;i<100;i++) {
System.out.println("主线程---"+i);
}
/**
*抢占式执行,每一次运行结果都不一样
*/
}
步骤总结:
(1)继承Thread类
(2) 重写run方法
(3)在main方法中创建对象
(4)调用start()方法,启动线程
- 实现Runnable接口
/**
* 创建线程的第二种方式:实现Runnable接口
* @author 1
*
*/
public class MyRunnable implements Runnable{
//1.实现Runnable接口
//2.重写run方法
@Override
public void run() {
for(int i=0;i<10;i++) {
//这里就不能使用this.getId(),因为没有继承Thread类
System.out.println("线程id:"+Thread.currentThread().getId()+"线程名称:"+Thread.currentThread().getName()+".........."+i);
}
}
}
public static void main(String[] args) {
//3.创建Runnable对象
MyRunnable mr=new MyRunnable();
//4.创建线程对象
Thread thread=new Thread(mr, "我的子线程1");
//5.调用start()
thread.start();
for(int i=0;i<100;i++) {
System.out.println("main"+"---------------"+i);
}
}
//优化代码(使用了匿名内部类)
Runnable runnable=new Runnable() {
public void run() {
for(int i=0;i<10;i++) {
//这里就不能使用this.getId(),因为没有继承Thread类
System.out.println("线程id:"+Thread.currentThread().getId()+"线程名称:"+Thread.currentThread().getName()+".........."+i);
}
}
};
//创建线程对象
Thread thread=new Thread(runnable, "我的子线程");
//启动线程
thread.start();
7.线程的基本状态
- 初始状态:线程对象被创建,即为初始状态,只在堆中开辟内存,与常规对象无异
- 就绪状态:调用start方法之后,进入就绪状态。等到OS选中,并分配时间片
- 运行状态:获得时间片之后,进入运行状态,如果时间片到期,则回到就绪状态
- 终止状态:主线程main()或独立线程run()结束,进入终止状态,并释放持有的时间片
8.线程的常见方法
- 休眠:(当前线程主动休眠xx毫秒)
Thread.sleep();//参数的单位是毫秒
-
放弃:(当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片)
Thread.yield(); -
加入:(允许其他线程加入到当前线程中,并阻塞当前线程,直到加入的线程执行完毕,当前线程才会继续执行)
对象.join();
- 优先级(线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多)
线程对象.setPriority();
9.同步方式
- 同步代码块:
synchronized (this) {//共享资源必须是引用类型
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"....还剩"+ticket+"张票");
ticket--;
}
案例:
package com.hp.runnable;
/**
* 实现4个窗口共卖100张票
* @author 15163095307
*
*/
public class SellTickets {
public static void main(String[] args) {
//1.创建Runnable对象
Runnable runnable=new Runnable() {
private int ticket=100;
// Object obj=new Object();
@Override
public void run() {
while(true) {
synchronized (this) {//共享资源必须是引用类型
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"....还剩"+ticket+"张票");
ticket--;
}
}
}
};
//2.创建线程对象
Thread thread=new Thread(runnable,"窗口1");
Thread thread1=new Thread(runnable,"窗口2");
Thread thread2=new Thread(runnable,"窗口3");
Thread thread3=new Thread(runnable,"窗口4");
//3.启动线程
thread.start();
thread1.start();
thread2.start();
thread3.start();
}
}
- 同步方法
在方法返回值类型前面加上synchornized
public synchornized boolean sale(){
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"....还剩"+ticket+"张票");
ticket--;
}
案例:
package com.hp.runnable;
public class Sale {
/**
* 实现4个窗口共卖100张票
* @author 15163095307
*
*/
private static int ticket=100;
public static void main(String[] args) {
//1.创建Runnable对象
Runnable runnable=new Runnable() {
// Object obj=new Object();
@Override
public void run() {
while(true) {
if(!sale()) {
break;
}
}
}
};
//2.创建线程对象
Thread thread=new Thread(runnable,"窗口1");
Thread thread1=new Thread(runnable,"窗口2");
Thread thread2=new Thread(runnable,"窗口3");
Thread thread3=new Thread(runnable,"窗口4");
//3.启动线程
thread.start();
thread1.start();
thread2.start();
thread3.start();
}
public synchronized static boolean sale() {
if(ticket<=0) {
return false;
}
System.out.println(Thread.currentThread().getName()+"....还剩"+ticket+"张票");
ticket--;
return true;
}
}
10.线程池
为什么会有线程池的概念呢?
线程是宝贵的内存资源,单个线程约占1MB的空间,过多分配易造成内存溢出;
频繁的创建以及销毁线程会增加虚拟机回收频率,资源开销,造成程序性能的下降。
- 线程池:线程容器,可设定线程分配的数量上限;将预先创建的线程对象存入池中,并重用线程池中的线程对象;避免频繁的创建和销毁
- 线程池的原理:将任务交给线程池,由线程池分配线程,运行任务,并在当前任务结束后复用线程(比如一个线程池中有三个预先创建的线程,外边有4个任务执行,那么此时会随机选择三个任务,三个线程对象分别执行任务,剩下的那个任务等待,直到某个线程执行完某个任务,那么等待的任务再被执行,这就实现了线程的复用)
package com.hp.xianchengchi;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 演示线程池的创建
* Executor:线程池的根接口,execute()
*
* ExecutorService:包含了管理线程池的一些方法,比如submit shutdown
*
* Executors:创建线程池的工具类
* (1)创建固定线程个数线程池
* (2)创建缓存线程池,由任务的多少来决定,个数不固定
* (3)创建单线程池
* (4)创建调度线程池 调度:周期,定时执行
* @author 1
*
*/
public class Demo1 {
public static void main(String[] args) {
//1.创建固定线程个数线程池
// ExecutorService es = Executors.newFixedThreadPool(4);
//1.1创建缓冲线程池,线程个数是由任务的个数来决定的
ExecutorService es= Executors.newCachedThreadPool();
//2.创建任务
Runnable runnable=new Runnable() {
private int tickets=100;
@Override
public void run() {
while(true) {
if(tickets<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖了"+tickets+"票");
tickets--;
}
}
};
for(int i=1;i<=4;i++) {
//3.提交任务
es.submit(runnable);
}
//4.关闭线程池
es.shutdown();//等待所有任务执行完毕,然后关闭线程池
}
}