进程:
系统中正在运行的一个应用程序
计算机会为每一个进程分配独立的资源空间来支持进程的执行
每个进程的执行都需要CPU(中央处理器)的支持
目前的计算机大部分都是支持多进程的 一边听歌 一边看笔记
线程:
线程基于进程的,线程是进程的基本单位
同属于一个进程的多个线程共享该进程的资源
多个线程同时执行 CPU在同一时刻只能执行一个线程 执行哪个线程完全是随机的
CPU执行效率相当的高 就认为多个线程是在并发执行
注意:之前写的所有程序在执行时都是一个线程 因为代码都写在main方法中,每次执行程序其实都是在执行主线程
自定义线程:Thread
方式一:
1、定义类继承Thread类
2、重写run方法
3、创建自定义线程类的对象
4、调用start方法来启动线程
参考代码:
public class ThreadDemo1 {
public static void main(String[] args) {
//3、创建自定义线程类的对象
MyThread1 thread1 = new MyThread1();
//4、调用start方法来启动线程
//程序中会有两个线程 main thread1
thread1.start();
}
}
//自定义线程类
// 1、定义类继承Thread类
// 2、重写run方法
// 3、创建自定义线程类的对象
// 4、调用start方法来启动线程
class MyThread1 extends Thread{
// 2、重写run方法
@Override
public void run() {
//当前线程要执行的逻辑代码
System.out.println("hello world!");
}
}
方式二:Runnable接口
1、定义类实现Runnable接口
2、实现抽象run方法
3、创建自定义线程类的对象
4、启动线程
参考代码:
public class ThreadDemo2 {
public static void main(String[] args) {
//3、创建自定义线程类的对象
MyThread2 thread2 = new MyThread2();
//4、启动线程
//通过Thread有参构造将自定义线程类包了一把
new Thread(thread2).start();
}
}
//自定义线程类
// 1、定义类实现Runnable接口
// 2、实现抽象run方法
// 3、创建自定义线程类的对象
// 4、启动线程
class MyThread2 implements Runnable{
// 2、实现抽象run方法
@Override
public void run() {
//当前线程要执行的逻辑代码
System.out.println("hello world!");
}
}
方式三:实现Callable接口
方式一和方式二要求必须掌握 方式三暂时了解即可
1、定义类Callable接口
2、实现抽象call方法-作用等同于run方法
call方法可以指定返回值 也可以抛出异常
3、启动执行线程
参考代码:
public class ThreadDemo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//启动执行线程
//获取ExecutorService对象
ExecutorService es = Executors.newCachedThreadPool();
//提交线程 并获取线程的返回值
MyThread3 thread3 = new MyThread3();
Future<String> future = es.submit(thread3);
//输出打印返回值
String str = future.get();
System.out.println("线程的返回值为:"+str);
}
}
//实现Callable接口
// 方式一和方式二要求必须掌握 方式三暂时了解即可
// 1、定义类Callable接口
// 2、实现抽象call方法-作用等同于run方法
// call方法可以指定返回值 也可以抛出异常
// 3、启动执行线程
//Callable接口的泛型用于指定call方法的返回值数据类型
class MyThread3 implements Callable<String>{
// 2、实现抽象call方法-作用等同于run方法
// call方法可以指定返回值 也可以抛出异常
@Override
public String call() throws Exception {
System.out.println("hello world!");
return "hello";
}
}
总结三种线程自定义方式的对比:
方式一:
优点:直接 启动简单
缺点:占用父类的位置 不能有返回值 不能抛出异常
方式二:
优点:java中实现多个接口 不占用父类 灵活性 扩展性强
缺点:不能有返回值 不能抛出异常
方式三:
优点:有返回值 抛出异常
缺点:代码复杂
要求:
方式一和方式二要求必须掌握 方式三暂时了解即可
Thread类的重点API:
构造方法:
public Thread(Runnable target)--启动通过实现Runnable接口定义的线程
public Thread(String name)-创建线程对象且指定线程的名称
public Thread(Runnable target,String name)
方法:
public static Thread currentThread()-获取当前线程对象
public final String getName()-获取线程的名称
public final void setName(String name)-指定线程的名称
public static void sleep(long millis)-让线程暂停指定的毫秒数
注意:
1、一个线程对象可以创建多个线程
一个线程只能启动一次
多线程
练习:
G101次 100张票 三个窗口同时售票
注意:
票数变量需要被多个线程共享
解决方案:
1、将票数设置为类的属性,启动多个线程使用同一个对象
2、将票数设置为类的静态属性,无论创建多少个对象都能保证共享同一个票数值
多线程并发安全问题:
以引出的案例为例,
现象:
同一张票被多个窗口重复出售了
票出售总数会增多
原因:
总结:
线程抢占CPU时间、位置完全是随机
多个线程同时操作共享的资源
synchronized-同步锁机制
1、同步代码块
格式:
synchronized (锁对象){
代码块;
}
锁对象:
Object类型 任意类型的对象
要求所有线程必须是同一个锁对象
特点:
在同步代码块中的代码,只能被一个线程执行,而其他线程只能在锁外等待
注意:
锁对象一般在开发中设计为共享资源的对象
2、同步方法
格式:
权限修饰符 synchronized 返回值数据类型 方法名(参数列表){
方法体;
}
特点:
如果有一个线程已经在执行当前方法,则其他线程不能执行当前方法
锁对象:
this
3、静态同步方法
格式:
权限修饰符 static synchronized 返回值数据类型 方法名(参数列表){
方法体;
}
特点:
如果有一个线程已经在执行当前方法,则其他线程不能执行当前方法
锁对象:
类名.class
线程通信
线程1:
问问题 AskProblem
线程2:
男女性别的切换 ChangeStu
等待唤醒机制:
方法:
public final void wait()-让当前线程进入等待
public final void notify()-唤醒一个线程
public final void notifyAll()-唤醒所有线程
注意:
1、通过锁对象来调用以上方法
2、wait方法进入等待有两层含义
当前线程不再抢占CPU
释放锁资源
参考代码理解
守护线程、线程的优先级、线程的状态
守护线程:
java中常见的守护线程:垃圾回收器
方法:
public final void setDaemon(boolean on)
--on值为true 则表示当前线程为守护线程
注意:
没有被设置为守护线程的线程都是被守护线程
一旦被守护线程结束 守护线程会随之结束
线程优先级:
1-10级
整体执行的概率上讲,执行的概率越高;
不能保证优先级高的一定先执行
方法:
public final int getPriority()-获取线程的优先级
默认情况下 线程的优先级是5
public final void setPriority(int newPriority)
设置线程的优先级
注意:
优先级通常不设置 效果不明显
线程的状态
wait:
进入等待
释放锁
通过notify唤醒之后才能进入就绪
sleep:
暂时停止
不会释放锁资源
当时间计时结束 回到就绪状态