1.并行,并发的区别
并发 concurrency:
一台处理器上同时处理任务, 这个同时实际上是交替处理多个任务,程序中可以同时拥有两个或者多
个线程,当有多个线程在操作时,如果系统只有⼀个CPU,则它根本不可能真正同时进行 一个以上的线
程,它只能把CPU运行 时间划分成若干个时间段,再将时间段分配给各个线程执行 。
并行 parallellism:
多个CPU上同时处理多个任务,一个CPU执行 ⼀个进程时,另一个CPU可以执行 另⼀个进程,两个进程互不抢占CPU资源,可以同时进行 。
在垃圾收集器中,用户线程与垃圾收集线程 的并行与并发关系:
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。
并发(Concurrent):指用户线程与垃圾收集线程同时执行,用户程序在继续执行,而垃圾收集程序运行于另一个CPU上。
2.实现多线程有哪几种方式
2.1.继承Thread
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("zbw");
thread.start();
}
}
2.2.实现Runnable接口
public class MyRunnable implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName());
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.setName("runnable");
thread.start();
//Lambda表达式
new Thread(()->{
System.out.println(Thread.currentThread().getName());
}).start();
}
}
2.3.通过Callable和FutureTask方式
public class MyTask implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println("通过Callable实现多线程,名称:"+Thread.currentThread().getName());
return "我是返回值";
}
public static void main(String[] args) {
MyTask myTask = new MyTask();
FutureTask<Object> futureTask = new FutureTask<>(myTask);
Thread thread = new Thread(futureTask,"CallableThread");
thread.start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2.4.通过线程池创建线程
public class ThreadPoolDemo implements Runnable {
@Override
public void run() {
System.out.println("通过Callable实现多线程,名称:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i=0;i<10;i++){
executorService.execute(new ThreadPoolDemo());
}
executorService.shutdown();
}
}
3.java线程常见的基本状态有哪些,这些状态分别是做什么的?
1.新建(NEW),表示线程被创建出来还没真正启动的状态,可以认为它是个 Java 内部状态。
2.就绪(RUNNABLE),表示该线程已经在 JVM 中执行,当然由于执行需要计算资源,它可能是正在运行,也可能还在等待系统分配给它 CPU 片段,在就绪队列里面排队。在其他一些分析中,会额外区分一种状态 RUNNING,但是从 Java API 的角度,并不能表示出来。
3.阻塞(BLOCKED),这个状态和我们前面两讲介绍的同步非常相关,阻塞表示线程在等待 Monitor lock。比如,线程试图通过 synchronized 去获取某个锁,但是其他线程已经独占了,那么当前线程就会处于阻塞状态。
4.等待(WAITING),表示正在等待其他线程采取某些操作。一个常见的场景是类似生产者消费者模式,发现任务条件尚未满足,就让当前消费者线程等待(wait),另外的生产者线程去准备任务数据,然后通过类似 notify 等动作,通知消费线程可以继续工作了。Thread.join() 也会令线程进入等待状态。
5.计时等待(TIMED_WAITING),其进入条件和等待状态类似,但是调用的是存在超时条件的方法,比如 wait 或 join 等方法的指定超时版本。
6.终止(TERMINATED),不管是意外退出还是正常执行结束,线程已经完成使命,终止运行,也有人把这个状态叫作死亡。
线程状态.jpg
4.Java线程的常见方法
sleep()
属于线程Thread的方法,让线程暂缓执行,等待预计时间之后再恢复,交出CPU使用权,不会释放锁,进入阻塞状态TIME_WAITGING,睡眠结束变为就绪Runnable。
yield()
属于线程Thread的方法,暂停当前线程的对象,去执行其他线程,交出CPU使用权,不会释放锁,和sleep类似。
作用:让相同优先级的线程轮流执行,但是不保证⼀定轮流。
注意:不会让线程进入阻塞状态,直接变为就绪Runnable,只需要重新获得CPU使用权。
join()
属于线程Thread的方法
在主线程上运行调用该方法,会让主线程休眠,不会释放已经持有的对象锁,让调用join方法的线程先执行完毕,再执行其他线程。类似让特种车辆优先通过
wait()
属于Object的方法,当前线程调用对象的wait方法,会释放锁,进入线程的等待队列,需要依靠notify或者notifyAll唤醒,或者wait(timeout)时间自动唤醒。
notify()
属于Object的方法,唤醒在对象监视器上等待的单个线程,选择是任意的。
notifyAll()
属于Object的方法,唤醒在对象监视器上等待的全部线程。
5.能举例几个多线程的业务场景吗?
异步任务:用户注册、记录日志
定时任务:定期备份日志、备份数据库
服务器编程:Socket网络编程,⼀个连接⼀个线程
6.了解volatile关键字不?
当一个变量被定义为volatile之后,它将具备两种特性。
第一是保证此变量对所有线程的可见性,这里的可见性是指,当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。volatile变量的运算,在并发下一样是不安全的。
第二个是禁止指令重排序优化,普通的变量仅仅会保证在该方法的执行过程中,所有依赖赋值结果的地方,都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。因为在一个线程的方法执行过程中无法感知这点,这也就是Java内存模型中描述的所谓的“线程内表现为串行的语义”。
假定T表示一个线程,V和M表示两个volatile变量,那么在进行Java内存模型的8种操作时,需要满足如下规则:
1.只有当线程T对变量V执行的前一个动作是load时,T才能对V执行use动作;并且,只有T对V的后一个动作时use时,T才能对V执行load操作。也就是说use和load,read动作必须连续一起出现。(这条规则要求在工作内存中,每次使用V前,都必须先从主内存刷新最新的值,用于保证能看到其他线程对变量V操作的修改后的值)
2.只有T对V执行的前一个动作是assign时,T才能对V执行store;并且,只有T对V的后一个动作是store时,T才能对V执行assign操作。也就是说assign和store,write动作必须连续一起出现。(这条规则要求在工作内存中,每次修改V后都必须立刻同步回主内存中,用于保证其他线程可以看到,自己对变量V的修改)
3.假定A是T对V的use操作,F是与A关联的load操作,P是与F关联的read操作;B是对W的use操作,G是与B关联的load操作,Q是与P关联的read操作。如果A先于B,那么P先于Q。(这条规则要求volatile修饰的变量,不会被指令重排优化,保证代码的执行顺序与程序的顺序相同)
12-规则3.jpg
网友评论