目录
一、进程和线程概念
1.1 进程
①进程是正在运行的程序
②它是操作系统分配资源的基本单位
1.2 线程
线程,又成轻量级进程(Light Weight Process),是进程中的一条执行路径,也是CPU的基本调度单位。一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,成为多线程。
1.3 例子
①迅雷---是一个进程,当中的多个下载任务即为线程
②JVM---Java虚拟机是一个进程,当中默认包含主线程(main),可通过代码创建多个独立线程,与main并发执行。
二、线程
2.1 Java中如何实现多线程
第一种:继承Thread类并重写run方法
第二种:实现Runnable接口
第三种:实现Callable接口
2.1.1 继承Thread类并重写run方法
① 获取当前线程的名称
第一种:通过父类Thread中的getName()可以获取线程名称。必须为Thread的子类
第二种: 通过Thread类中的静态方法currentThread获取当前线程,getName()获取线程名。任意处获取线程名。
②为线程起名
通过setName()为线程起名
2.1.2 实现Runnable接口
记住:能实现接口的就不要继承父类。因为父类只需要单继承。拓展性比较差
例子:
多个窗口公卖同一张票,而且也会出现超卖的现象。----多线程操作公共数据时出现的线程安全问题。
2.2 Thread类中常用的方法
2.1.1 休眠
public static void sleep(long millis)
当前线程主动休眠millis毫秒
2.2.2 放弃
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
A和B交替的概率高
2.2.3 加入
public final void join()
允许其他线程加入到当前线程中,直到其他线程执行完毕后,当前线程才会执行
2.2.4 优先级
线程对象.setPriority()
线程优先级1-10,默认为5,优先级越高,表示获取CPU的概率越高
2.2.5 线程守护
线程对象.setDaemon(true);设置为守护线程
- 线程有两类:用户线程(前台线程)和守护线程(后台线程)
- 如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。
- 垃圾回收线程属于守护线程。
三、多线程安全问题
- 当多线程并发访问临界资源时,如果破坏原子操作,可难会造成数据不一致
- 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
- 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省
3.1 多线程安全问题演示:
需求: A线程将“Hello”存入数组;B数据将“World”存入数组。
public class Test05 {
//创建数组
private static String arr[]=new String[2];
//下标
private static int index=0;
public static void main(String[] args) throws InterruptedException {
//需求: A线程将“Hello”存入数组;B数据将“World”存入数组。
Thread t1 = new Thread(new Runnable() { //匿名内部类
@Override
public void run() {
if (arr[index]==null){
arr[index]="hello";
index++;
}
}
});
Thread t2 = new Thread(new Runnable() { //匿名内部类
@Override
public void run() {
if (arr[index]==null){
arr[index]="world";
index++;
}
}
});
//开启线程
t1.start();
t2.start();
t1.join();
t2.join();
//t1和t2执行完毕才执行下方的打印
System.out.println(Arrays.asList(arr));
}
}
3.2 在程序应用中,如何保证线程的安全性
3.2.1 同步方式
同步代码块:synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原子操作)
}
注意:
- 每个对象都有一个互斥锁标记,用来分配给线程的
- 只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
- 线程退出同步代码块,会释放相应的互斥锁标记
修改上方演示代码 :加入synchronized
public class Test05 {
//创建数组
private static String arr[]=new String[2];
//下标
private static int index=0;
public static void main(String[] args) throws InterruptedException {
//需求: A线程将“Hello”存入数组;B数据将“World”存入数组。
Thread t1 = new Thread(new Runnable() { //匿名内部类
@Override
public void run() {
//synchronized 同步(互斥锁)
synchronized (arr){ //同步代码块 必须获取()锁资源才能进入同步代码块中
//这里的操作必须原子操作要求
if (arr[index]==null){
arr[index]="hello";
index++;
}
}
}
});
Thread t2 = new Thread(new Runnable() { //匿名内部类
@Override
public void run() {
synchronized (arr){
if (arr[index]==null){
arr[index]="world";
index++;
}
}
}
});
//开启线程
t1.start();
t2.start();
t1.join();
t2.join();
//t1和t2执行完毕才执行下方的打印
System.out.println(Arrays.asList(arr));
}
}
解决案例2的线程安全问题 (this)临界资源对象