回顾Java知识点,面试题汇总Day9(持续更新)
1.sleep()和wait()的区别?
sleep和wait的功能类似,都是让线程暂停执行任务。
- sleep是Thread类提供的方法,wait是Object类提供的方法
- sleep是直接作用于线程对象本身,wait作用于线程正在访问的资源。
调用A对象的wait:让当前正在访问的A对象的线程休眠,同时它拥有一个前提,当前线程必须拥有A对象,所以wait方法只能在同步方法或同步代码块中调用,否则会抛出异常。
- wait会释放锁,sleep不会释放锁。
package com;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 10; i++) {
a.test(i);
}
}).start();
}
}
class A{
public synchronized void test(int i){
if(i == 5){
//wait没有时间,会一直休眠下去
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + "-----------------");
}
}
i == 5时,调用A的wait方法,会让正在访问A的线程休眠,并且永久不会解除阻塞。
2.如何让线程解除阻塞?
- 指定wait的时间,wait(long millis)之后会自动解决阻塞,类似sleep
wait->Object,A继承了Object,A中自带wait方法。
sleep->Thread
class A{
public synchronized void test(int i){
if(i == 5){
//wait没有时间,会一直休眠下去
try {
this.wait(5000);//指定时间后,阻塞5秒继续输出(this也可以去掉)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + "-----------------");
}
}
- 调用notify方法唤醒线程
package com;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 10; i++) {
a.test(i);
}
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(7); //输出到5时,等待两秒,再继续输出
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.test1();
}).start();
}
}
class A{
public synchronized void test(int i){
if(i == 5){
//wait没有时间,会一直休眠下去
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1); //表示1秒输出一个值
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + "-----------------");
}
public synchronized void test1(){ //必须使用synchronized修饰
this.notify();
}
}
wait和notify方法必须放到同步方法或者同步代码中,否则会抛出异常。
3.synchronized锁定的是谁?
- 如果synchronized修饰非静态方法,则锁定的是方法的调用者
import java.util.concurrent.TimeUnit;
public class SyDemo {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
data.func1();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
data.func2();
},"B").start();
}
}
class Data{
public synchronized void func1(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1=======");
}
public synchronized void func2(){
System.out.println("2++++");
}
}

- 如果synchronized修饰的是静态方法,则锁定的是类,无论有多少个对象,都会同步。
4.ConcurrentModificationException并发修改异常
并发修改:多个线程对同一个对象进行修改。
import java.util.ArrayList;
import java.util.List;
public class Bfxg {
public static void main(String[] args) {
List<String>list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("a");
System.out.println(list);
}).start();
}
}
}

由于同时读,同时写,产生了异常。
ArrayList是线程不安全的集合,当多个线程同时操作集合时,会出现数据不准确的情况。
5.ConcurrentModificationException如何解决?
-
将ArrayList替换成线程安全的集合Vector
Vector中add方法是synchronized修饰
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
ArrayList中的add方法
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
-
使用Collections.synchronizedList
import java.util.*;
public class Bfxg {
public static void main(String[] args) {
// List<String> list = new ArrayList<>();
// List<String>list = new Vector<>();
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("a");
System.out.println(list);
}).start();
}
}
}
-
JUC工具类CopyOnWriteArrayList
java.util.concurrent JDK的一个包,存放的都是关于并发的工具类。
写时复制。
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class Bfxg {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("a");
System.out.println(list);
}).start();
}
}
}
copyOnWrite写时复制,当我们往一个容器中添加元素的时候,不直接往当前容器中添加,而是将当前容器复制,向新容器中添加元素,添加完成之后,再将原容器的引用指向新的容器。
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
可以对CopyOnWrite容器进行并发的读,因为当前容器不会添加任何元素,添加元素都是针对复制出来的新集合进行操作,所以CopyOnWrite容器也是一种读写分明的思想,读和写操作的是不同容器。
6.JUC常见并发编程工具包

-
CountDownLatch
减法计数器,JUC的工具类,可以用来倒计时,当两个线程同时执行时,如果我们要确保一个线程优先执行,设置一个计数器,当计数器清零的时候,再执行另一个线程。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(100);//因为需要循环100次
new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("+++++++++++Thread");
countDownLatch.countDown();
}
}).start();
//判断是否清零
try {
countDownLatch.await();//注意:这里是await,不是wait,如果使用wait,会报异常,且不会执行main------
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("main-----------");
}
}
}
-
CyclicBarrier
加法计数器
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
System.out.println("放行");
});
for (int i = 0; i < 30; i++) {
final int temp = i;
new Thread(()->{
System.out.println("-->" + temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
-
Semaphore
计数信号量,实际开发中主要用来完成限流操作,即限制可以访问某些资源的线程数量。
- 初始化
- 获得许可
- 释放
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 15; i++) {
new Thread(()->{
try {
semaphore.acquire();//获取许可
System.out.println(Thread.currentThread().getName() + "进店购买");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "出店");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
-
读写锁
接口ReadWriteLock,实现类ReentrantReadWriteLock,可以多线程同时读,但是同一时间只能有一个线程进行写入操作。
读写锁功能也是为了实现线程同步,只不过粒度更细,可以分别给读和写操作设置不同的锁机制。
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
public static void main(String[] args) {
Cache cache = new Cache();
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
cache.write(temp,String.valueOf(temp));
}).start();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(()->{
cache.read(temp);
}).start();
}
}
}
class Cache{
private Map<Integer,String> map = new HashMap<>();
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//写操作
public void write(Integer key,String value){
readWriteLock.writeLock().lock();
System.out.println(key + "开始写入");
map.put(key, value);
System.out.println(key + "写入完毕");
readWriteLock.writeLock().unlock();
}
//读操作
public void read(Integer key){
readWriteLock.readLock().lock();
System.out.println(key + "开始读取");
map.get(key);
System.out.println(key + "读取完毕");
readWriteLock.readLock().unlock();
}
}
7.线程池
预先创建好一定数量的线程对象,存入缓冲池,需要用的时候直接从缓冲池中取出,用完后还回到缓冲池中,供下一次任务使用。
优点
- 提高线程的利用率
- 提高响应速度
- 便于统一管理线程对象
- 可控制最大并发量
工作流程
- 初始化线程池,创建默认数量的线程对象
- 当任务过多的时候,额外补充线程数量
- 当任务趋于正常时,额外补充的线程会自动销毁
初始化线程数量
最大线程数量
线程池的实现(3种)
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Pool {
public static void main(String[] args) {
// //单例线程池,只有一个线程对象
// ExecutorService executorService = Executors.newSingleThreadExecutor();
// ExecutorService executorService = Executors.newFixedThreadPool(5);
//缓存线程池,不同性能的电脑分配的不一样
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int temp = i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName() + ":" + temp);
});
}
executorService.shutdown();
}
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor 线程池的原生类
核心参数
- corePoolSize:核心池大小
- maximumPoolSize:线程池最大线程数
- keepAliveTime:空闲线程的存活时间
- unit:时间单位
- workQueue:阻塞队列
- threadFactory:线程工厂
- handler:拒绝策略
import java.util.concurrent.*;
public class Pool {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2,5,1L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 2; i++) {
executorService.execute(()->{
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在办理业务");
});
}
executorService.shutdown();
}
}更多推荐

所有评论(0)