多线程是面试绕不开的必问题目,今天系统的来让大家了解下多线程
一.多线程
首先明确一个概念,就是线程与进程的关系
1.进程
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
2.线程
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
线程调度
分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),
Java使用的为
抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,
只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时
刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使
用率更高。
3.创建线程的方式
1.继承Thread类
public class MyThread extends Thread{
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
for (int i = 0;i<10;i++){
System.out.println("一起加油"+i);
}
}
}
主线程
public class Demo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0;i<10;i++){
System.out.println("一定成功"+i);
}
}
}
运行结果如下
由结果可知,其实他们是交替运行的,也就是作为的抢占式分配.
2.使用Runnable接口
运行结果如下
由此得出结论,也是抢占式的方法.请大家思考一个问题,相比于继承Thread相比有什么好处? 答案如下
1.通过创建任务,然后线程分配的方式实现的多线程,更适合多个线程同时执行相同任务
2.避免单继承所来的局限性,因为java只有单继承,所以当一个类继承了Thread他就不能继承其他类了,接口的多实现java可以.
3.任务与线程本身是分离的,提高了程序的健壮性
4.后续学习的线程池技术,接受Runnable的任务,不接受Thread类型的线程
3带返回值的线程Callable
之前的线程和主线程其实是并发的,但是Callable这种方式也可以实现一起走.也可以等结束,拿到结果
- 编写类实现Callable接口 , 实现call方法 class XXX implements Callable {
@Override public call() throws Exception { return T; } }
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象 FutureTask future = new FutureTask<>(callable);
3. 通过Thread,启动线程 new Thread(future).start();
4举例子
public class Demo7 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> c= new MyCallable();
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
Integer j = task.get();
System.out.println("返回值"+j);
for (int i = 0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
for (int i = 0;i<10;i++){
Thread.sleep(1000);
System.out.println(i);
}
return 100;
}
}
}
这是一个启动的线程例子
二 线程安全问题
1.线程不安全经典例子
对于线程不安全问题,有一个非常经典的例子,就是卖票问题,
假设一共有10张票,我们开始卖票,假设3个窗口同时卖,意味着创建3个线程
public class Demo2 {
//线程不安全问题
public static void main(String[] args) {
Runnable runnable = new Ticket();
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
static class Ticket implements Runnable{
private int count = 10;//总共十张票;
@Override
public void run() {
while (count>0){
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,剩余的票数"+count);
}
}
}
}
出现了问题,票数不能为负数
2 解决方案
1同步代码块
public class Demo2 {
//线程不安全问题
//解决方案,同步代码块,
//synchronized方法解决
public static void main(String[] args) {