三木之注意事项基础--并发、多线程问题

volatile

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

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

 

原子操作类AtomicInteger

保证线程安全

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

 

ThreadLocal

public class Member {
	public int count=0;
	public static ThreadLocal threadLocal=new ThreadLocal(){
		@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形式 键值对 存放值。

 

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关键字声明,目的是第一时间获取修改的内容,性能非常好。

 

 

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, 优先级)执行。

 

 

 

你可能感兴趣的:(Java并发)