Java多线程总结

本文深入探讨Java中的多种并发工具和技术,包括synchronized关键字的使用及其限制、ReentrantLock的高级特性、生产者-消费者模式的经典实现、ThreadLocal的工作原理、常用的并发容器特性,以及线程池的设计与配置技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、synchronized

1、synchronized可以对对象,代码块、方法等加锁。对静态对象、字符串,锁定引用变化等会加锁失败。

2、调用问题。

(1)同步和非同步方法可以同时调用。

(2)一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.,也就是说synchronized获得的锁是可重入的。

(3) 子类可以调用父类的同步方法

3、对写操作加锁,对读操作不加锁会导致脏读。

4、程序在执行过程中,如果出现异常,默认情况锁会被释放。在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。要加入try catch捕获异常。

5、采用细粒度的锁,可以使线程争用时间变短,从而提高效率。同步代码块中的语句越少越好。

注:可以使用AtomXXXX保证原子性

二、ReentrantLock

1、public class ReentrantLock implements Lock, java.io.Serializable  

   ReentrantLock实现Lock的接口

2、使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行, 可以根据tryLock的返回值来判定是否锁定, 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中。

3、public ReentrantLock(true)    //实现 公平锁

4、lockInterruptibly() //当获取锁时线程被打断会抛出 InterruptedException 。

5、tryLock (long timeout, TimeUnit  unit),   如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

三、生产者,消费者

public class MyContainer<T> {
	final private LinkedList<T> lists = new LinkedList<>();
	final private int MAX = 10; // 最多10个元素
	private int count = 0;

	public synchronized void put(T t) {
		while (lists.size() >= MAX) { // 在线程被唤醒的前后都持续检查条件是否被满足
			try {
				this.wait(); // 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		lists.add(t);
		++count;
		this.notifyAll(); // 通知消费者线程进行消费
	}

	public synchronized T get() {
		T t = null;
		while (lists.size() <= 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		t = lists.removeFirst();
		count--;
		this.notifyAll(); // 通知生产者进行生产
		return t;
	}

	public static void main(String[] args) {
		MyContainer<String> c = new MyContainer<>();

		// 启动生产者线程
		for (int i = 0; i < 2; i++) {
			new Thread(() -> {
				for (int j = 0; j < 25; j++)
					c.put(Thread.currentThread().getName() + " " + j);
			}, "生产者生产了" + i).start();
		}

		// 启动消费者线程
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				for (int j = 0; j < 5; j++)
					System.out.println(c.get());
			}, "消费者" + i).start();
		}

	}
}

四、ThreadLocal

1、ThreadLocal是使用空间换时间,synchronized是使用时间换空间。

2、ThreadLocal通过set和get设置和获取当前线程的值,与其他线程无关。通过ThreadLocalMap保存数据。

五、并发容器

ConcurrentHashMap:分段锁,不会锁住整张表。

ConcurrentSkipListMap:高并发且排序。

CopyOnWriteArrayList:适合写少读多的环境。

CountDownLatch:多线程下实现类似计数的功能。

BlockingQueue:通常用于一个线程生产对象,另外一个线程消费这些对象的场景。

DelayQueue:用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。每次从队列里取出来都是最先要过期的元素。

TransferQueue:先找到head 节点,如果 head 节点是匹配的操作,就直接赋值,如果不是,添加到队列中。

六、线程池

1、自定义构造方法,

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)。

线程池参数,

  •  corePoolSize:核心线程数。核心线程会一直存活,即使没有任务需要执行,当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理。设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。queueCapacity:任务队列容量(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行
  • maxPoolSize:最大线程数。 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务, 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
  •  keepAliveTime:线程空闲时间。 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize

 

 rejectedExecutionHandler:任务拒绝处理器

    两种情况会拒绝处理任务:1.当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务。

   2.当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务

线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常

拒绝策略(直接丢弃,丢弃队列中最老的任务,抛异常,将任务分给线程来执行),

  • ThreadPoolExecutor类有几个内部实现类来处理这类情况
  •  AbortPolicy 丢弃任务,抛运行时异常
  • CallerRunsPolicy 执行任务
  • DiscardPolicy 忽视,什么都不会发生
  • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
  • 实现RejectedExecutionHandler接口,可自定义处理器

 

2、newFixedThreadPool构造方法。

public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

    }

3、newCachedThreadPool构造方法
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

    }

4、newSingleThreadExecutor构造方法
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));

    }

5、newScheduledThreadPool构造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());

    }

6、newWorkStealingPool构造方法
 public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);

    }

7、ForkJoinPool.execute(ForkJoinTask<?> task) 分任务执行

      ForkJoinTask<V>已知直接子类:CountedCompleter , RecursiveAction , RecursiveTask

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值