*、易混概念:什么是java内存结构?什么是java内存模型?
- java内存结构属于jvm内存分配;
- java内存模型属于多线程可见性,决定一个线程对于另一个线程是否可见;
- Java内存模型:主内存(存放共享的全局变量) 、私有本地内存(本地线程私有变量);
*、什么是线程安全?解释一下多线程的线程安全问题?
多个线程共享同一个全局变量,对全局变量进行修改的时候,可能会受到其它线程的干扰,导致数据发生错误。
解决多个线程数据安全问题 - 加锁: synchronized、lock;
多线程(加锁)缺点: 降低程序效率、阻塞、抢锁、效率不高
*、多线程5中运行状态。
*、创建一个多线程:
class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("子Thread1: +"+i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
Thread2 t2 = new Thread2();
//==implements Runnable 与 extends Thread 调用方式不同
Thread t = new Thread(t2);
t.start();
for (int i = 0; i < 30; i++) {
System.err.println("主Demo1: +"+i);
}
}
}
*、面试题:a、b、c三个线程,如何让a先执行完,再执行b 执行完,再执行c?
回答:使用 join() 方法;
a线程 调用 b线程的join(),a线程会等待b线程执行完毕之后再执行。
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("【a子线程】: +"+i);
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 30; i++) {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【bb子线程】: +"+i);
}
}
});
Thread t3 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 30; i++) {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【ccc子线程】: +"+i);
}
}
});
t1.start();
t2.start();
t3.start();
}
*、面试题:多线程写一个火车票程序。
class Th implements Runnable {
private static AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
while(count.intValue()<=50){
synchronized ("ok") {
if (count.incrementAndGet()<=50) {
System.out.println(Thread.currentThread().getName()+"出售第"+count.intValue()+"张车票");
} else {
System.err.println(Thread.currentThread().getName()+"火车票卖完了");
}
}
}
}
}
public class 火车票 {
public static void main(String[] args) {
new Thread(new Th(), "【窗口1】").start();
new Thread(new Th(), "【窗口2】").start();
new Thread(new Th(), "【窗口3】").start();
}
}
*、Lock与synchronized有以下区别:
- Lock是一个接口;可以知道线程有没有拿到锁;必须手动释放锁(通常在finally中释放);可以让等待锁的线程响应中断;Lock是块范围内的;
- synchronized是关键字;不知道线程有没有拿到锁;会自动释放锁(代码执行完毕或者抛出异常由jvm放弃锁),不能够手动释放锁;线程会一直等待下去;synchronized能锁住类、方法和代码块;
Lock能提高多个线程读操作的效率。
释放对象的锁有两种情况:
- 程序执行完同步代码块会释放代码块。
- 程序在执行同步代码块是出现异常,JVM会自动释放锁去处理异常。
*、多线程之间的通讯方式
回答一:
参考:https://www.cnblogs.com/lgyxrk/p/10404839.html
- 同步:这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。
- while轮询的方式:线程A不断地改变条件,线程ThreadB不停地通过while语句检测这个条件(list.size()==5)是否成立 ,从而实现了线程间的通信。
- wait / notify机制:当条件未满足时(list.size() !=5),线程A调用wait() 放弃CPU,并进入阻塞状态;当条件满足时,线程B调用 notify()通知 线程A,所谓通知线程A,就是唤醒线程A,并让它进入可运行状态。
- 管道通信:使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信
回答二:
参考:https://blog.csdn.net/jisuanji12306/article/details/86363390
- 使用 volatile 关键字
-
使用Object类的wait() 和 notify() 方法
-
使用JUC工具类 CountDownLatch
-
使用 ReentrantLock 结合 Condition
-
基本LockSupport实现线程间的阻塞和唤醒
*、Runnable接口和Callable接口的区别。
返回值:Runnable没有返回值,Callable有返回值;
异常:Runnable没有容错机制,意味着如果出现异常必须立即处理;Callable有容错机制,意味着出现异常之后可以向上抛出;
启动方式:Runnable可以通过Thread来启动,也可以通过线程池的execute.submit来处理;Callable线程只能通过线程池的submit来处理
*、Java锁的种类
悲观锁、乐观锁、分段锁、重入锁、读写锁、CAS无锁、自旋锁、排他锁、分布式锁
乐观锁 和 悲观锁:
- 悲观锁: 每次取数据的时候,都会上锁;
- 乐观锁: CAS无锁机制————版本标识;
- 区别: 1、如果查询数量小,可以使用悲观锁;2、乐观锁使用版本控制;
重入锁 与 非重入锁 区别:
- 重入锁也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响;
- 非重入锁 死锁;
读写锁的机制: ReentrantReadWriteLock
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock r = rwl.readLock();
static Lock w = rwl.writeLock();
CAS无锁机制:与自旋锁配合使用;
CAS体系中的三个参数:CAS(V,E,N):
V表示要更新的变量,E表示预期值,N表示新值。
仅当V值等于E值时,才会将V的值设为N,
如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。
最后,CAS返回当前V的真实值。
自旋锁: 让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。
*、线程池
线程池的核心是 ThreadPoolExecutor
创建线程池的方法:java.util.concurrent.Executors
- Executors.newCachedThreadPool(),创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- Executors.newSingleThreadScheduledExecutor(),创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- Executors.newFixedThreadPool(4),创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- Executors.newScheduledThreadPool(3),创建一个定长线程池,支持定时及周期性任务执行。
线程池配置多少合适?
- 方法一: CPU密集:线程数和cpu核数相同;
- 方法二:IO密集:2*cpu数;
*、说几个 java.util.concurrent.* 下的工具类(高级篇)
concurrent是 util 下的并发包。
java.util.concurrent.ConcurrentHashMap
- 比 HashMap 线程安全;
java.util.concurrent.CountDownLatch
- 实现计数功能;可以使线程按顺序执行,比join()书写简便
/**
* CountDownLatch 可以使线程按顺序执行,比join()书写简便
*
*/
public class TestCountDownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程," + Thread.currentThread().getName() + "开始执行...");
countDownLatch.countDown();// 每次减去1
System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程," + Thread.currentThread().getName() + "开始执行...");
countDownLatch.countDown();
System.out.println("子线程," + Thread.currentThread().getName() + "结束执行...");
}
}).start();
System.out.println("等待子线程执行完毕...");
countDownLatch.await();// 如果countDownLatch不为0,一直等待
System.out.println("两个子线程执行完毕....");
System.out.println("继续主线程执行..");
}
}
java.util.concurrent.CyclicBarrier
- 始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。
- 当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
- cyclicBarrier.await() 控制所有线程都执行完毕,然后才继续执行
/**
* cyclicBarrier.await() 控制所有线程都执行完毕,然后才继续执行
*
*/
class Write extends Thread {
private CyclicBarrier cyclicBarrier;
public Write(CyclicBarrier cyclicBarrier){
this.cyclicBarrier=cyclicBarrier;
}
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + ",正在写入数据");
try {
Thread.sleep(3000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("线程" + Thread.currentThread().getName() + ",写入数据成功.....");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.err.println("所有线程执行完毕..........");
}
}
public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
new Write(cyclicBarrier).start();
}
}
}
java.util.concurrent.Semaphore
- new Semaphore(5); 可以用来构建一些对象池,
- semaphore.acquire(); //申请资源
- semaphore.release();// 释放资源
/**
* new Semaphore(5); 可以用来构建一些对象池,
* semaphore.acquire(); //申请资源
* semaphore.release();// 释放资源
*/
class Parent implements Runnable {
Semaphore wc;
String name;
public Parent(String name, Semaphore wc) {
this.name = name;
this.wc = wc;
}
@Override
public void run() {
try {
// 剩下的资源(剩下的茅坑)
int availablePermits = wc.availablePermits();
if (availablePermits > 0) {
System.out.println(name + "天助我也,终于有茅坑了...");
} else {
System.out.println(name + "怎么没有茅坑了...");
}
// 申请茅坑 如果资源达到3次,就等待
wc.acquire();
System.out.println(name + "终于轮我上厕所了..爽啊");
Thread.sleep(new Random().nextInt(1000)); // 模拟上厕所时间。
System.out.println(name + "厕所上完了...");
wc.release();
} catch (Exception e) {
}
}
}
public class TestSemaphore {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 10; i++) {
Parent parent = new Parent("第" + i + "个人,", semaphore);
new Thread(parent).start();
}
}
}
java.util.concurrent.ConcurrentLinkedDeque 和 BlockingQueue
- ConcurrentLinkedDeque 不阻塞、无界;
- BlockingQueue 可以阻塞、有界;
/**
* peek(); //消费后不删除元素
* poll(); //消费后删除元素
*/
public class TestConcurrentLinkedDeque {
public static void main(String[] args) {
//无界队列
ConcurrentLinkedDeque<Object> quene = new ConcurrentLinkedDeque<>();
quene.offer("码云");
quene.offer("张杰");
quene.offer("艾姐");
System.out.println(quene.size());
System.out.println(quene.peek()); //消费后不删除元素
System.out.println(quene.poll()); //消费后删除元素
System.out.println(quene.peek());
System.out.println(quene.peek());
System.out.println(quene.size());
// BlockingQueue s = new BlockingQueue(3);
}
}
*、Java并发解决方式?并发包有哪些?(高级篇)
原子类(D2 - D6)、lock、并发包;
*、new ThreadLocal()
ThreadLocal为每一个线程提供一个局部变量
//class ResNum {
// public int num = 0;
//
// public String getNum(){
// num = num + 1;
// return num+"";
// }
//}
class ResNum {
public int num = 0;
//ThreadLocal为每一个线程提供一个局部变量
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
protected Integer setInitialValue() {
return 0;
}
};
public Integer getNum(){
int num = threadLocal.get()+1;
threadLocal.set(num);
return num;
}
}
class Thread2 extends Thread {
private ResNum resNum;
public Thread2(ResNum resNum) {
this.resNum = resNum;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.err.println(getName()+"num:"+resNum.getNum());
}
}
}
public class TestThreadLocal {
public static void main(String[] args) {
ResNum res = new ResNum();
new Thread2(res).start();
new Thread2(res).start();
new Thread2(res).start();
}
}
*、wait与sleep区别?
wait用于同步中,可以释放锁资源; sleep 不会释放锁的资源;
wait可以被notify唤醒;sleep是时间到期及被唤醒;
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
*、wait() 和 notify()
wait让当前线程从运行状态 变成 休眠状态;
notify让当前线程从休眠状态 变成 运行状态;
*、什么多线程之间通讯?
多个线程 对 同一个共享资源,每个线程对共享资源的操作不同;
多线程通讯的应用: 生产者 与 消费者。 消息中间件。
*、三大特性之原子性和可见性 —— volatile关键字 和 AtomicInteger
volatile关键字: 保证线程之间可见,但不保证原子性、不安全;
new AtomicInteger():可以保证线程之间可见,并且保证原子性;
以下是 volatile 关键字实例:
class Thread1 extends Thread {
private volatile boolean flag = true; //volatile关键字,强制刷新
// private boolean flag = true;
public void setFlag(boolean flag){
this.flag = flag;
}
private int i = 0;
@Override
public void run() {
System.out.println("子线程开始执行,"+"此时flag:"+flag);
while (flag) {
System.err.println(i+" flag: "+flag);
i++;
}
System.out.println("子线程结束....,"+"此时flag:"+flag);
}
}
public class VolatileTest {
public static void main(String[] args) throws InterruptedException {
Thread1 t = new Thread1();
t.start();
Thread.sleep(20);
t.setFlag(false);
}
}
以下是 AtomicInteger 实例:
class Thread3 extends Thread {
// private volatile static int count = 0; //volatile 不能保证数据原子性, 改为下面 AtomicInteger方式
private static AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
// count++;
count.incrementAndGet();
}
System.out.println(count);
}
}
public class AtomicIntegetTest {
public static void main(String[] args) throws InterruptedException {
Thread3 [] t = new Thread3[10];
for (int i = 0; i < t.length; i++) {
t[i] = new Thread3();
}
for (int i = 0; i < t.length; i++) {
t[i].start();
}
}
}
*、三大特性之有序性 —— join()方法
主线程 调用 子线程的join(),主线程会等待子线程执行完毕之后 再执行。
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 30; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【子线程】: +"+i);
}
}
});
t1.start();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i==3) {
try {
t1.join(); //主线程 调用 子线程的join(),主线程会等待子线程执行完毕之后 再执行。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.err.println("主线程: +"+i);
}
System.err.println("【主线程执行完毕....】");
}
*、多线程三大特性?
原子性、可见性、有序性。
- 原子性: 独一无二,保证线程安全;
- 可见性: Java 内存模型;
- 有序性: join、wait、notify(多线程之间通讯)
*、什么是守护线程、非守护线程?
守护线程(gc线程)随着主线程一起销毁;非守护线程(用户线程)和主线程互不影响;
public static void main(String[] args) {
Thread t = new Thread(new Runnable() { //用户线程(非守护线程)
public void run() {
for (int i = 0; i < 30; i++) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【子线程】: +"+i);
}
}
});
t.start();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("主线程: +"+i);
}
System.err.println("【主线程执行完毕....】");
}
1、创建多线程的方法
- 以下是继承 thread 类
class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("Thread1: +"+i);
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
// t1.run(); //直接调用,属于同步执行,没有开启线程 所以不会异步
t1.start();
for (int i = 0; i < 30; i++) {
System.err.println("Demo1: +"+i);
}
}
}
- 以下是实现 Runnable 接口:
class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("子Thread1: +"+i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
Thread2 t2 = new Thread2();
//==implements Runnable 与 extends Thread 调用方式不同
Thread t = new Thread(t2);
t.start();
for (int i = 0; i < 30; i++) {
System.err.println("主Demo1: +"+i);
}
}
}
- 以下是使用匿名内部类的方式创建多线程
public class Demo3 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("子Thread1: +"+i);
}
}
});
t.start();
for (int i = 0; i < 30; i++) {
System.err.println("主Demo3: +"+i);
}
}
}
*、什么是线程安全?解释一下多线程的线程安全问题?
多个线程共享同一个全局变量,对全局变量进行修改的时候,可能会受到其它线程的干扰,导致数据发生错误。
解决多个线程数据安全问题 - 加锁: synchronized、lock;
多线程(加锁)缺点: 降低程序效率、阻塞、抢锁、效率不高