一、程序、进程、线程
- 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码。
- 进程((process)正在内存中运行的应用程序,如运行中的QQ,运行中的音乐播 放器。进程是操作系统进行资源分配的最小单位。
- 线程(thread)进程可进一步细化为线程,是一个进程内部的最小执行单元,是操 作系统进行任务调度的最小单元,隶属于进程。
注:
- 一个进程可以包含多个线程。
- 一个线程只能属于一个进程,线程不能脱离进程而独立运行。
- 每一个进程至少包含一个线程(称为主线程)。
- 在主线程中可以创建并启动其它的线程。
- 一个进程内的所有线程共享该进程的内存资源
二、线程的创建
代码示例如下:
/*
创建线程方式1:
MyThread 继承 Thread(线程)
重写Thread类中的run()方法, 在run()方法中来编写我们需要执行的任务代码
*/
public class MyThread extends Thread{
//run方法是线程执行任务的方法
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("MyThread:"+i);
}
}
}
import java.io.Serializable;
/*
创建一个任务,实现Runnable接口
重写Runnable接口中的run方法
*/
public class MyTask implements Runnable{
@Override
public void run() {
//在任务中,我想知道当前是哪个线程正在执行我
Thread thread = Thread.currentThread();//在任务中,通过currentThread()获得当前正在执行的线程
System.out.println(thread.getName());
/*for (int i = 0; i <10000 ; i++) {
System.out.println("mytask:"+i);
}*/
}
}
三、Thread类中的方法
四、线程的优先级
- 事实上,计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能 执行任务。
- 优先级较高的线程有更多获得CPU的机会,反之亦然。
- 优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级 都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级。
- 调度策略:
时间片
抢占式:高优先级的线程抢占CPU - java的调度方法:
同优先级线程组成先进先出队列,使用时间片策略
对高优先级,使用优先调度的抢占式策略
代码示例如下:
public class MyTask implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();//获得当前正在执行此任务的线程
/*if(thread.getName().equals("线程1")){
try {
Thread.sleep(4000);//让线程休眠 1000毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
System.out.println(thread.getName()+":"+thread.getPriority());//获得线程优先级
}
}
五、线程状态
六、多线程的概念
多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多 个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行 执行的线程来完成各自的任务 。
何时需要多线程
- 程序需要同时执行两个或多个任务。
- 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、 网络操作、搜索等。
多线程的优点
- 提高程序的响应。
- 提高CPU的利用率。
- 改善程序结构,将复杂任务分为多个线程,独立运行。
多线程的缺点
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多; 多线程需要协调和管理,所以需要跟踪管理线程,使得cpu开销变大; 线程之间同时对共享资源的访问会相互影响,如果不加以控制会导致数据 出错
七、线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制, 即各线程间要有先来后到。
同步就是排队+锁:
- 几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作。
- 为了保证数据在方法中被访问时的正确性,在访问时加入锁机制。
同步锁:同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用 来充当锁标记)。
同步执行过程
- 第一个线程访问,锁定同步对象,执行其中代码。
- 第二个线程访问,发现同步对象被锁定,无法访问。
- 第一个线程访问完毕,解锁同步对象。
- 第二个线程访问,发现同步对象没有锁,然后锁定并访问 。
代码示例如下:
public class PrintNumThread extends Thread{
static int num = 0;
static String object=new String();
/*
wait(); 让线程等待,自动释放锁,必须要其他的线程唤醒
notify(); 唤醒等待中的线程(调用了wait方法的线程),如果有多个等待,唤醒优先级高的
notifyAll() 唤醒所有等待的线程
这三个方法都是Object类中定义的方法,
这三个方法必须在同步代码块中使用,
这三个方法必须通过为锁的对象调用
*/
@Override
public void run() {
while (true){
synchronized (object){
object.notify();//唤醒等待中的线程
if(num<=100){
num++;
System.out.println(Thread.currentThread().getName()+":"+num);
}else{
break;
}
try {
object.wait();//让线程等待.同时释放了锁,等待的线程不能自己醒来,必须让另一个线程唤醒.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/*
柜台角色 共享数据
*/
public class Counter {
int num = 1; //代表商品的数量 例如为0
//负责生产商品的方法 锁对象是this add() 和 sub() 用的是同一把锁
public synchronized void add(){
if(num==0){
System.out.println("生产者生产了一件商品");
num = 1;//生产一件商品
this.notify();//唤醒消费者线程
}else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//负责消费商品的方法
public synchronized void sub(){
if(num>0){
System.out.println("消费者拿走了一件商品");
num=0;//消费者拿走了商品
this.notify();
}else{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
生产者线程
*/
public class ProductorThread extends Thread{
Counter counter;
public ProductorThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while (true){
counter.add();//生产者线程 一直生产
}
}
}
public class CustomerThread extends Thread{
Counter counter;
public CustomerThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while (true){
counter.sub(); //消费者线程一直消费
}
}
}