三木之注意事项基础--集合

执行时间短(加锁代码),线程数少,用自旋锁(atomic)

执行时间长,线程数多,用系统锁(synchronized)

volatile

volatile的作用:volatile变量进行读时,会有一个主内存到工作内存到拷贝动作,进行写后,会有一个工作内存刷新主内存到动作。

(即:即时刷新变量的值,主内存<==>私有内存,保证线程之间可见,但不保证原子性)

原子操作类AtomicInteger

保证线程安全

AtomicInteger atomicInteger = new AtomicInteger(0);	//加上static转全局变量	
atomicInteger.incrementAndGet();// 加一
atomicInteger.decrementAndGet();//减一

ThreadLocal

ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题

public class Member {
	public int count=0;
	public static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(){
		@Override
		protected Integer initialValue() {
			// TODO Auto-generated method stub
			return 0;
		}
	};
	public String getcount(){
		count=threadLocal.get()+1;
		threadLocal.set(count);
		return count+"";
	}
	
}


public class ThreadLockTest {
	
	public static void main(String[] args) {
		Member member = new Member();
		MemberVo memberVo1 = new MemberVo(member);
		MemberVo memberVo2 = new MemberVo(member);
		MemberVo memberVo3 = new MemberVo(member);
		memberVo1.start();
		memberVo2.start();
		memberVo3.start();
	}
}

class MemberVo extends Thread{
	private Member member;
		
	MemberVo(Member member){
		this.member=member;
	}
	
	@Override
	public void run() {
		for(int i=0;i<3;i++){
System.out.println("ThreadName:"+currentThread().getName()+"||"+"member:"+member.getcount());
		}
	}
}

原理:各个线程的私有内存取变量主内存的值,然后用ThreadLocalMap形式 <key,value>键值对 存放值。

阿里TTL线程池(解决异步执行时上下文传递的问题)

阿里TTL线程池

在使用线程池等会池化复用线程的执行组件情况下,transmittable-thread-local(简称TTL)提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。TTL主要解决了:

  1. 透传上下文信息,run方法调用结束时自动restore上下文信息
  2. 线程池使用CallerRunsPolicy时,避免了手动回收上下文信息会将主线程的上下文信息清空

List接口的实现类有ArrayList与LinkedList,Vector

ArrayList:底层由数组结构实现,数组在内存中的存储顺序是连续的,对集合中的元素可以进行快速访问,更适合用来随机查询数据。

LinkedList:底层由双向链表结构实现,通过节点来存储下一个元素的位置,对集合中的元素可以方便的增加与删除,更适合用于大量修改。

Vector:Vector与ArrayList的区别就是Vector是线程安全的集合,在需要线程安全而且对效率要求比较低的情况下,使用Vector。

Vector与ArrayList区别

Vector线程安全(其中Add方法源 带锁 synchronized)、ArrayList线程不安全(效率高)

实现原理都是通过数组来实现,效果都是--查询速度快,增删改速度慢

CopyOnWriteArrayList

添加、删除加锁,查询不加锁(但使用volatile可保证数据准确性

HashTable与HashMap、ConcurrentHashMap区别

HashTable线程安全,HashMap线程不安全、ConcurrentHashMap线程安全。

HashTable:基本所有的方法都带锁,线程安全,效率太低。

HashMap:不带锁,不安全,但效率高。

ConcurrentHashMap:带锁(分为16个不同的锁),线程安全,且效率高。(推荐使用

其中:ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个
小的HashTable,它们有自己的锁。
只要多个修改操作发生在不同的段上,它们就可以并
发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。
这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变
量使用volatile关键字声明,目的是第一时间获取修改的内容,性能非常好。

runnable和callable的区别:

两者都是接口,都可以用来创建线程。Callable属于Executor框架中的类。Callable与Runnable接口的功能类似,区别主要在于以下几点:
(1)Callable可以在任务结束之后提供一个返回值。Runnable不可以。
(2)Callable中call()方法可以抛出异常。Runnable中的run()方法不可以。
(3)Callable运行过程中可以拿到一个Future对象,可以用来监视目标线程调用call()方法的情况。

相同点:

执行业务代码时间长时,避免主线程一直处于等待状态而去使用java的Thread/callable产生的线程执行

不同点

callable作用是线程执行的方法带返回值,而Thread是不能带返回值。

callable使用分析(重要):

执行业务代码时间长时,主线程会一直执行业务代码,直到主线程执行到future.get() (即需获取子线程执行的业务返回结果)主线程才可继续往下执行,在主线程拿子线程结果(future.get())之前,主线程都是不需要等待的。

代码如下:

public class TestMain {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<Integer> future = executor.submit(new AddNumberTask());
        System.out.println(1.Thread.currentThread().getName() + "线程执行其他任务");
        Integer integer = future.get();
        System.out.println(2.Thread.currentThread().getName() + "线程执行其他任务");
        System.out.println(integer);
        // 关闭线程池
        if (executor != null)
            executor.shutdown();
    }

}

class AddNumberTask implements Callable<Integer> {

    public AddNumberTask() {

    }

    @Override
    public Integer call() throws Exception {
        System.out.println("3.####AddNumberTask###call()");
        Thread.sleep(5000);
        System.out.println("4.####AddNumberTask###call()");
        return 5000;
    }

}

CountDownLatch、CyclicBarrier和 Semaphore

CountDownLatch:

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他2个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

public class Test002 {

	public static void main(String[] args) throws InterruptedException {
		System.out.println("等待子线程执行完毕...");
		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();

		countDownLatch.await();// 调用当前方法主线程阻塞  countDown结果为0, 阻塞变为运行状态
		System.out.println("两个子线程执行完毕....");
		System.out.println("继续主线程执行..");
	}

}

CyclicBarrier:

CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。 

CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 

CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

class Writer extends Thread {
	private CyclicBarrier cyclicBarrier;
	public Writer(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 (Exception e) {
		}
		System.out.println("所有线程执行完毕..........");
	}

}

public class Test001 {

	public static void main(String[] args) {
		CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
		for (int i = 0; i < 5; i++) {
			Writer writer = new Writer(cyclicBarrier);
			writer.start();
		}
	}

}

Semaphore(信号量,可以控同时访问的线程个数):

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。它的用法如下:

availablePermits函数用来获取当前可用的资源数量

wc.acquire(); //申请资源 ,通过 acquire() 获取一个许可,如果没有就等待

wc.release();// 释放资源 ,release() 释放一个许可。

	// 创建一个计数阈值为5的信号量对象  
    	// 只能5个线程同时访问  
    	Semaphore semp = new Semaphore(5);  
    	  
    	try {  
    	    // 申请许可  
    	    semp.acquire();  
    	    try {  
    	        // 业务逻辑  
    	    } catch (Exception e) {  
    	  
    	    } finally {  
    	        // 释放许可  
    	        semp.release();  
    	    }  
    	} catch (InterruptedException e) {  
    	  
    	}  

案例:

需求: 一个厕所只有3个坑位,但是有10个人来上厕所,那怎么办?假设10的人的编号分别为1-10,并且1号先到厕所,10号最后到厕所。那么1-3号来的时候必然有可用坑位,顺利如厕,4号来的时候需要看看前面3人是否有人出来了,如果有人出来,进去,否则等待。同样的道理,4-10号也需要等待正在上厕所的人出来后才能进去,并且谁先进去这得看等待的人是否有素质,是否能遵守先来先上的规则。

代码:

class Parent implements Runnable {
	private String name;
	private Semaphore wc;
	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 TestSemaphore02 {
	public static void main(String[] args) {
		// 一个厕所只有3个坑位,但是有10个人来上厕所,那怎么办?假设10的人的编号分别为1-10,并且1号先到厕所,10号最后到厕所。那么1-3号来的时候必然有可用坑位,顺利如厕,4号来的时候需要看看前面3人是否有人出来了,如果有人出来,进去,否则等待。同样的道理,4-10号也需要等待正在上厕所的人出来后才能进去,并且谁先进去这得看等待的人是否有素质,是否能遵守先来先上的规则。
         Semaphore semaphore = new Semaphore(3);
		for (int i = 1; i <=10; i++) {
			 Parent parent = new Parent("第"+i+"个人,",semaphore);
			 new Thread(parent).start();
		}
	}
}

LinkedBlockingQueue

(链接:java多线程之线程安全队列LinkedBlockingQueue)

1、LinkedBlockingQueue 是线程安全的,实现了先进先出的特性

2、BlockingQueue提供了以下几个方法:

add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常
offer       添加一个元素并返回true       如果队列已满,则返回false
poll         移除并返问队列头部的元素    如果队列为空,则返回null
peek       返回队列头部的元素             如果队列为空,则返回null
put         添加一个元素                      如果队列满,则阻塞
take        移除并返回队列头部的元素     如果队列为空,则阻塞

线程池

第一:降低源消耗。通重复利用已建的线程降低线建和造成的消耗。
第二:提高响速度。当任到达,任可以不需要等到线建就能立即行。
第三:提高线程的可管理性线程是稀缺源,如果无限制地建,不会消耗系统资源,
会降低系定性,使用线程池可以一分配、调优控。但是,要做到合理利用
线程池,必须对实现原理了如指掌。

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

Executor框架详解

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值