java高并发(一)

(1)线程基础(一)

1.1 线程安全
线程安全的概念:
在多个线程访问一个类(对象或方法)的时候,这个类始终都能表现出正确的行为,那么这个类(对象或者是方法)就是线程安全的。
synchronized:可以再任意对象以及方法生上加上锁,而加锁的这段代码称为“互斥锁”或者是临界区

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个对象始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区"或"临界区"
 * @author alienware
 *
 */
public class MyThread extends Thread{
    
    private int count = 5 ;
    
    //synchronized加锁
    public void run(){
        count--;
        System.out.println(this.currentThread().getName() + " count = "+ count);
    }
    
    public static void main(String[] args) {
        /**
         * 分析:当多个线程访问myThread的run方法时,以排队的方式进行处理(这里排对是按照CPU分配的先后顺序而定的),
         *         一个线程想要执行synchronized修饰的方法里的代码:
         *         1 尝试获得锁
         *         2 如果拿到锁,执行synchronized代码体内容;拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,
         *            而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题)
         */
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread,"t1");
        Thread t2 = new Thread(myThread,"t2");
        Thread t3 = new Thread(myThread,"t3");
        Thread t4 = new Thread(myThread,"t4");
        Thread t5 = new Thread(myThread,"t5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

实例总结:
当多个线程访问myThread 的run方法时,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized 修饰的方法的代码。首先是尝试获得锁,如果拿到锁,执行synchronized代码体的内容,拿不到这把锁,这个线程就会不断的尝试获得这把锁。知道拿到为止,而且是多个线程同时去竞争这把锁(也就是会有锁竞争的问题)。


1.2 多个线程多个锁
       多个线程多个锁,每个线程都可以拿到自己指定的锁,分别获取锁之后,执行sycnhronized方法体的内容。

/**
 * 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
 * 所以代码中哪个线程先执行synchronized关键字的方法,哪个线程就持有该方法所属对象的锁(Lock),
 * 
 * 在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。
 * @author alienware
 *
 */
public class MultiThread {

	private int num = 0;
	
	/** static 类级别的锁,可以按照aa bb输出*/
	public synchronized void printNum(String tag){
		try {
			
			if(tag.equals("a")){
				num = 100;
				System.out.println("tag a, set num over!");
				Thread.sleep(1000);
			} else {
				num = 200;
				System.out.println("tag b, set num over!");
			}
			
			System.out.println("tag " + tag + ", num = " + num);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//注意观察run方法输出顺序
	public static void main(String[] args) {
		
		//俩个不同的对象
		final MultiThread m1 = new MultiThread();
		final MultiThread m2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("a");
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			@Override 
			public void run() {
				m2.printNum("b");
			}
		});		
		
		t1.start();
		t2.start();
		
	}
}

实例总结: 
关键字synchronized 取得的锁都是对象锁,而不是一段代码(方法当做锁)
所以实例代码中那个线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),两个对象,线程就获取两个不同的锁,互不影响。
有一种情况是相同的锁,在静态的方法上添加synchronized关键字,表示类一级别的锁(锁定.class关键字)类一级别的锁。

 

1.3 对象锁的同步和异步
同步:synchronized 
同步的概念就是共享,我们要牢牢记住“共享”这个概念,如果不是共享的资源。就没有必要进行同步
异步:异步的概念就是独立,相互之间受制约,就如同我们学习http的时候,在页面
发起的ajax请求,我们还可以继续浏览和操作页面的内容,两者之间没有任何的关系
同步的目的就是为了线程安全,其实对于线程安全,需要满足两个特性: 
原子性
可见性

/**
 * 对象锁的同步和异步问题
 * @author alienware
 *
 */
public class MyObject {

    public synchronized void method1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    /** synchronized */
    public void method2(){
            System.out.println(Thread.currentThread().getName());
    }
    
    public static void main(String[] args) {
        
        final MyObject mo = new MyObject();
        
        /**
         * 分析:
         * t1线程先持有object对象的Lock锁,t2线程可以以异步的方式调用对象中的非synchronized修饰的方法
         * t1线程先持有object对象的Lock锁,t2线程如果在这个时候调用对象中的同步(synchronized)方法则需等待,也就是同步
         */
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                mo.method1();
            }
        },"t1");
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                mo.method2();
            }
        },"t2");
        
        t1.start();
        t2.start();
        
    }
    
}

实例总结
A线程首先持有Object对象的Lock锁,B线程如果在这个时候调用对ye(synchronized)方法需等待,也就是同步
A线程先持有Object对象的Lock锁,B线程可以异步的方式调用对象中非synchronized修饰的方法

1.4 脏读
对于对象的同步和异步的方法,我们在设计自己程序的时候,一定要考虑到问题的整体,不然就会出现数据不一致的错误
很经典的错误就是脏读

/**
 * 业务整体需要使用完整的synchronized,保持业务的原子性。
 * @author alienware
 *
 */
public class DirtyRead {

    private String username = "bjsxt";
    private String password = "123";
    
    public synchronized void setValue(String username, String password){
        this.username = username;
        
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        this.password = password;
        
        System.out.println("setValue最终结果:username = " + username + " , password = " + password);
    }
    // 需要在该方法上加上synchronized才能保证在set值得时候,不会执行get方法
    public void getValue(){
        System.out.println("getValue方法得到:username = " + this.username + " , password = " + this.password);
    }
    
    
    public static void main(String[] args) throws Exception{
        
        final DirtyRead dr = new DirtyRead();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                dr.setValue("z3", "456");        
            }
        });
        t1.start();
        Thread.sleep(1000);
        
        dr.getValue();
    }
    
}

实例总结:
在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为setValue/getValue
方法同时加锁synchronized 关键字,保证业务(services)的原子性,不会出现业务错误
(也是侧面保证业务的一致性8)


1.5 synchronized其他概念
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象的锁后,再次请求此对象时时可以再次得到该对象的锁,

/**
 * synchronized的重入
 * @author alienware
 *
 */
public class SyncDubbo1 {

    public synchronized void method1(){
        System.out.println("method1..");
        method2();
    }
    public synchronized void method2(){
        System.out.println("method2..");
        method3();
    }
    public synchronized void method3(){
        System.out.println("method3..");
    }
    
    public static void main(String[] args) {
        final SyncDubbo1 sd = new SyncDubbo1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                sd.method1();
            }
        });
        t1.start();
    }
}
/**
 * synchronized的重入
 * @author alienware
 *
 */
public class SyncDubbo2 {

    static class Main {
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class Sub extends Main {
        public synchronized void operationSub(){
            try {
                while(i > 0) {
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);        
                    this.operationSup();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Sub sub = new Sub();
                sub.operationSub();
            }
        });
        
        t1.start();
    }
    
    
}
synchronized出现异常,锁自动释放
public class Synchronized4 {
	private int i = 0;
	public synchronized void method1(){
		while(true){
				try{
					i++;
					Thread.sleep(1000);
					System.out.println("i="+i);
					if(i==10){
					Integer.parseInt("a");
					}
				}catch(Exception e){
					e.printStackTrace();
					// 第一种解决办法,记录日志
					System.out.println("log info"+i);
					// 第二种解决办法
					throws new RuntimeException();
                        // 第三种方法,直接跳过本次异常,继续执行
					continue;
				}
			}
	}
	public static void main(String[] args) {
		final Synchronized4 s = new Synchronized4();
		Thread t1 = new Thread(new Runnable(){
			public void run(){
				s.method1();
			}
		});
		t1.start();
	}
}
}
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10
log info10
java.lang.NumberFormatException: For input string: "a"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:492)
	at java.lang.Integer.parseInt(Integer.java:527)
	at bingfa1.Synchronized4.method1(Synchronized4.java:12)
	at bingfa1.Synchronized4$1.run(Synchronized4.java:24)
	at java.lang.Thread.run(Thread.java:745)
i=11

  

实例总结,对于web应用程序异常释放锁的情况,如果不及时的处理,很可能
对你的应用程序业务逻辑产生严重的错误,例如你现在执行一个对列任务,
很多对象都在等待一个对象正确执行完毕再去释放锁,但是第一个对象由于
异常的出现,导致业务逻辑没有正常的执行完毕,就释放了锁,那么可想而知后续的
对象执行都是错误的逻辑,所以这一点一定要引起注意,在编写代码的时候,
一定要考虑周全。

1.6
使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步
的方法执行一个很长时间的任务,那么B线程就必须等待很长时间才能执行,这样情况下可以使用
synchronized代码块去优化代码的执行时间,也就是通常所说的减小锁的粒度。

synchronized可以使用任意的Object进行加锁,用法比较灵活

synchronized可以使用任意的Object进行加锁,用法比较灵活

public class Lock1(){
public void method1(){
    synchronized(this){
        try{
        }catch(Exeption e){
         e.printStackTrace();
        }
    }    
}
public void method2(){
    synchronized(Lock1.class){
        try{
        }catch(Exeption e){
         e.printStackTrace();
        }
    }    
}

private Object lock = new Object();
public void method3(){
    synchronized(lock){
        try{
        }catch(Exeption e){
         e.printStackTrace();
        }
    }    
}
}

另外特别注意的一个问题,就是不要使用String常量加锁,会出现s死循环,字符创常量在内存中存在常量池中,引用只有一个,使用新建字符串不会出现这个问题
public class StringLock {
    
    public void method1(){
        // new String("字符串常量")
        //synchronized ("字符串常量") {
            synchronized (new String("字符串常量")) {
            try{
                while(true){
                    System.out.println("当前线程:"+Thread.currentThread().getName()+"开始");
                }    
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        final StringLock s = new StringLock();
        Thread t1 = new Thread(new Runnable(){
            public void run(){
                s.method1();
            }
        });
        t1.start();
        final StringLock s1 = new StringLock();
        Thread t2 = new Thread(new Runnable(){
            public void run(){
                s1.method1();
            }
        });
        t2.start();
    }
}
锁对象的改变问题,当使用一个对象进行加锁的时候,
要注意对象本身发生改变的时候,
public class ChangeLock {
    private String lock = "lock"; 
    public void method1(){
            synchronized (lock) {
            try{
                    System.out.println("当前线程:"+Thread.currentThread().getName()+"开始");
                    lock = "change lock";// 修改了锁对象
                    Thread.sleep(1000);
                    System.out.println("当前线程:"+Thread.currentThread().getName()+"结束");
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        final ChangeLock s = new ChangeLock();
        Thread t1 = new Thread(new Runnable(){
            public void run(){
                s.method1();
            }
        });
        t1.start();
        final ChangeLock s1 = new ChangeLock();
        Thread t2 = new Thread(new Runnable(){
            public void run(){
                s1.method1();
            }
        });
        t2.start();
    }
}

那么持有的锁就不同,如果对象本身不发生改变,
那么依然是同步的,即使对象的属性发生了变化;
public class ObjcetLock {
    private String name;
    private String title;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public synchronized void checkObject(String name,String title){
            try{
                    this.setName(name);
                    Thread.sleep(1000);
                    this.setTitle(title);
                    System.out.println(Thread.currentThread().getName()+"的名称是"+title+"名字"+name);
            }
            catch(Exception e){
                e.printStackTrace();
            }
    }
    
    public static void main(String[] args) {
        final ObjcetLock s = new ObjcetLock();
        Thread t1 = new Thread(new Runnable(){
            public void run(){
                s.checkObject("11","11");
            }
        });
        t1.start();
        final ObjcetLock s1 = new ObjcetLock();
        Thread t2 = new Thread(new Runnable(){
            public void run(){
                s1.checkObject("22","22");
            }
        });
        t2.start();
    }
}

1.7 volatile关键字
概念:使变量在多个线程间可见

public class RunThread extends Thread{
  
    private boolean isRunning = true;
    private void setRunning(boolean isRunning){
        this.isRunning = isRunning;
    }
    
    public void run(){
        System.out.println("进入run方法..");
        int i = 0;
        while(isRunning == true){
            //..
        }
        System.out.println("线程停止");
    }
    
    public static void main(String[] args) throws InterruptedException {
        RunThread rt = new RunThread();
        rt.start();
        Thread.sleep(1000);
        rt.setRunning(false);
        System.out.println("isRunning的值已经被设置了false");
    }
    
    
}

运行结果

进入run方法..
线程停止
isRunning的值已经被设置了false

注意:程序没有停止,因为JDK1.5之后,为每一个线程中的变量开辟一块单独的内存来保存变量,所以拿到的变量还是true,

 private volatile boolean isRunning = true;
变量被volatile修饰之后,强制改变线程执行引擎到主内存中读取变量,在将主内存中修改之后的变量,加载到为线程独立开辟的内存中,也就是修改之后的值,
这时候程序停止。
 


/**
* volatile关键字不具备synchronized关键字的原子性(同步)
* @author alienware
*
*/
public class VolatileNoAtomic extends Thread{
private static volatile int count;
//private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count++ ;
//count.incrementAndGet();
}
System.out.println(count);
}

public void run(){
addCount();
}

public static void main(String[] args) {

VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}

for (int i = 0; i < 10; i++) {
arr[i].start();
}
}

}

 

 运行结果

1000
2000
3148
3683
5115
6115
4115
7115
8115
9115

分析:多个线程操作count++ ,volatile 不具备原子性

解决:使用private static AtomicInteger count = new AtomicInteger(0); 支持多线程并发的原子操作,

                        count.incrementAndGet();// ++ 之后的值可以返回

运行结果是:  

3000
4000
3000
3000
6000
5311
7000
9000
8000
10000

(结果一定是10000)

public class AtomicUse {

    private static AtomicInteger count = new AtomicInteger(0);
    
    //多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
    /**synchronized*/
    public synchronized int multiAdd(){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count.addAndGet(1);
            count.addAndGet(2);
            count.addAndGet(3);
            count.addAndGet(4); //+10
            return count.get();
    }
    
    
    public static void main(String[] args) {
        
        final AtomicUse au = new AtomicUse();

        List<Thread> ts = new ArrayList<Thread>();
        for (int i = 0; i < 100; i++) {
            ts.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(au.multiAdd());
                }
            }));
        }

        for(Thread t : ts){
            t.start();
        }
        
    }
}

运行结果

10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290
300
310
320
330
340
350
360
370
380
390
400
410
420
430
440
450
460
470
480
490
500
510
520
530
540
550
560
570
580
590
600
610
620
630
640
650
660
670
680
690
700
710
720
730
740
750
760
770
780
790
800
810
820
830
840
850
860
870
880
890
900
910
920
930
940
950
960
970
980
990
1000

volatile关键字虽然拥有多个线程间的可见性,但是却不具备同步性(也就是原子性),可以算的上是一个轻量级的
synchronized,性能上要比synchronized强的多,不会造成阻塞,(在许多开源的框架中,比如netty的底层代码就大量使用
volatile可见netty性能一定是不错的),这里需要注意的是,一般volatile用于只针对多个线程可见的变量操作,
并不能代替synchronized的同步功能。

实例总结:volitile关键字只具有可见性,没有原子性,要实现原子性,建议使用atomic类的系列对象,
支持原子性操作(注意atomic类只是保证本身方法的原子性,并不保证多次操作的原子性)

2.1 线程间的通讯:线程是操作系统中的独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通讯就成为整体的必用方式之一。当线程存在通讯指挥,系统间的交互性更强大,在提高CPU利用率的同事还会使开发人员对线程在处理的过程中进行有效的把控和监督。

使用wait/notify方法实现线程间的通信(注意这两个方法都是Object类中的方法,换句话说java为所有的对象提供的这两个方法)

1.wait和nofify必须配合synchronized关键字使用

2.wait方法释放锁,nofify方法不释放锁。

public class ListAdd1 {

    
    private volatile static List list = new ArrayList();    
    
    public void add(){
        list.add("bjsxt");
    }
    public int size(){
        return list.size();
    }
    
    public static void main(String[] args) {
        
        final ListAdd1 list1 = new ListAdd1();
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for(int i = 0; i <10; i++){
                        list1.add();
                        System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                        Thread.sleep(500);
                    }    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1");
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    if(list1.size() == 5){
                        System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止..");
                        throw new RuntimeException();
                    }
                }
            }
        }, "t2");        
        
        t1.start();
        t2.start();
    }
    
    
}

运行结果

当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程收到通知:t2 list size = 5 线程停止..
Exception in thread "t2" 当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
java.lang.RuntimeException
at com.bjsxt.base.conn008.ListAdd1$2.run(ListAdd1.java:43)
at java.lang.Thread.run(Unknown Source)
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..

下面是改进版

/**
 * wait notfiy 方法,wait释放锁,notfiy不释放锁
 * @author alienware
 *
 */
public class ListAdd2 {
    private volatile static List list = new ArrayList();    
    
    public void add(){
        list.add("bjsxt");
    }
    public int size(){
        return list.size();
    }
    
    public static void main(String[] args) {
        
        final ListAdd2 list2 = new ListAdd2();
        
        // 1 实例化出来一个 lock
        // 当使用wait 和 notify 的时候 , 一定要配合着synchronized关键字去使用
        //final Object lock = new Object();
        
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //synchronized (lock) {
                        for(int i = 0; i <10; i++){
                            list2.add();
                            System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素..");
                            Thread.sleep(500);
                            if(list2.size() == 5){
                                System.out.println("已经发出通知..");
                                countDownLatch.countDown();
                                //lock.notify();
                            }
                        }                        
                    //}
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }, "t1");
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //synchronized (lock) {
                    if(list2.size() != 5){
                        try {
                            //System.out.println("t2进入...");
                            //lock.wait();
                            countDownLatch.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止..");
                    throw new RuntimeException();
                //}
            }
        }, "t2");    
        
        t2.start();
        t1.start();
        
    }
    
}

运行结果

当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
已经发出通知..
当前线程:t1添加了一个元素..
当前线程:t2收到通知线程停止..
Exception in thread "t2" java.lang.RuntimeException
当前线程:t1添加了一个元素..
at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:71)
at java.lang.Thread.run(Unknown Source)
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..

2.2 使用notify和wait实现对列

public class MyQueue {
    
    //1 需要一个承装元素的集合 
    private LinkedList<Object> list = new LinkedList<Object>();
    
    //2 需要一个计数器
    private AtomicInteger count = new AtomicInteger(0);
    
    //3 需要制定上限和下限
    private final int minSize = 0;
    
    private final int maxSize ;
    
    //4 构造方法
    public MyQueue(int size){
        this.maxSize = size;
    }
    
    //5 初始化一个对象 用于加锁
    private final Object lock = new Object();
    
    
    //put(anObject): 把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间再继续.
    public void put(Object obj){
        synchronized (lock) {
            while(count.get() == this.maxSize){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //1 加入元素
            list.add(obj);
            //2 计数器累加
            count.incrementAndGet();
            //3 通知另外一个线程(唤醒)
            lock.notify();
            System.out.println("新加入的元素为:" + obj);
        }
    }
    
    
    //take: 取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入.
    public Object take(){
        Object ret = null;
        synchronized (lock) {
            while(count.get() == this.minSize){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //1 做移除元素操作
            ret = list.removeFirst();
            //2 计数器递减
            count.decrementAndGet();
            //3 唤醒另外一个线程
            lock.notify();
        }
        return ret;
    }
    
    public int getSize(){
        return this.count.get();
    }
    
    
    public static void main(String[] args) {
        
        final MyQueue mq = new MyQueue(5);
        mq.put("a");
        mq.put("b");
        mq.put("c");
        mq.put("d");
        mq.put("e");
        
        System.out.println("当前容器的长度:" + mq.getSize());
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                mq.put("f");
                mq.put("g");
            }
        },"t1");
        
        t1.start();
        
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Object o1 = mq.take();
                System.out.println("移除的元素为:" + o1);
                Object o2 = mq.take();
                System.out.println("移除的元素为:" + o2);
            }
        },"t2");
        
        
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        t2.start();
        
        
    }
    
    
    
}

 2.3 ThreadLocal

概念:线程局部变量,是一种访问多线程并发访问变量的解决办法,与其synchronized 等加锁的方式不同,TheadLocal完全不提供锁,

而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。

从性能上说,TheadLocal不具有绝对的优势,在并发不是很高的时候,加锁的性能会更好,但是作为一套与锁完全无关的

线程安全解决方案,在高并发量或者是竞争激烈的场景,使TheadLocal可以再一定程度上减少锁竞争。

public class ConnThreadLocal {

    public static ThreadLocal<String> th = new ThreadLocal<String>();
    
    public void setTh(String value){
        th.set(value);
    }
    public void getTh(){
        System.out.println(Thread.currentThread().getName() + ":" + this.th.get());
    }
    
    public static void main(String[] args) throws InterruptedException {
        
        final ConnThreadLocal ct = new ConnThreadLocal();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                ct.setTh("张三");
                ct.getTh();
            }
        }, "t1");
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    ct.getTh();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t2");
        
        t1.start();
        t2.start();
    }
    
}

运行结果:

t1:张三
t2:null

2.4   单例&多线程

单例模式,最常见的就是饥饿模式和懒汉模式,一个直接实例化对象,一个在调用方法时进行实例化对象。

在多线程模式中,考虑到性能和线程安全问题,我们一般选择下面两种比较经典的到单例模式,在提高性能的同时,

又保证了线程的安全。

static inner class;

public class Singletion {
    
    private static class InnerSingletion {
        private static Singletion single = new Singletion();
    }
    
    public static Singletion getInstance(){
        return InnerSingletion.single;
    }
    
}

double check instance;

public class DubbleSingleton {

    private static DubbleSingleton ds;
    
    public  static DubbleSingleton getDs(){
        if(ds == null){
            try {
                //模拟初始化对象的准备时间...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (DubbleSingleton.class) {
                if(ds == null){
                    ds = new DubbleSingleton();
                }
            }
        }
        return ds;
    }
    
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(DubbleSingleton.getDs().hashCode());
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(DubbleSingleton.getDs().hashCode());
            }
        },"t2");
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(DubbleSingleton.getDs().hashCode());
            }
        },"t3");
        
        t1.start();
        t2.start();
        t3.start();
    }
    
}

6244110
6244110
6244110

3.1 同步类容器

同步类容器都是线程安全的,但在某些场景下可能需要加锁来保护复合操作。

复合操作如:迭代(反复访问元素,遍历容器中所有的元素)跳转(根据指定的顺序找到当前元素的下一个元素)

以及条件运算。这些复合操作在多线程并发的修改容器是,可能会出现意外的行为,最经典的便是ConcurrentModificationExcetion,原因是当前容器迭代的过程中,被并发修改了内容,这是由于早期迭代器设计的时候并没有考虑到并发修改的问题。

同步类容器:古老的Vector.HashTable.这些容器的同步工鞥呢其实都是有JDK的Collctions.synchronized等工厂方法区创建实现的。

其底层机制无非就是用传统的synchronized关键字对每个公用的方法都进行同步,使得每次只能有一个线程访问容器的状态。这很明显不

满足我们今天互联网时代高并发的需求,在保证线程安全的同时,也必须要由足够好的性能。

/**
 * 多线程使用Vector或者HashTable的示例(简单线程同步问题)
 * @author alienware
 */
public class Tickets {

    public static void main(String[] args) {
        //初始化火车票池并添加火车票:避免线程同步可采用Vector替代ArrayList  HashTable替代HashMap
        
        final Vector<String> tickets = new Vector<String>();
        
        //Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

        for(int i = 1; i<= 1000; i++){
            tickets.add("火车票"+i);
        }
        
        for (Iterator iterator = tickets.iterator(); iterator.hasNext();) {
            String string = (String) iterator.next();
            tickets.remove(20);
        }
        
        for(int i = 1; i <=10; i ++){
            new Thread("线程"+i){
                public void run(){
                    while(true){
                        if(tickets.isEmpty()) break;
                        System.out.println(Thread.currentThread().getName() + "---" + tickets.remove(0));
                    }
                }
            }.start();
        }
    }
}

运行结果:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.Vector$Itr.checkForComodification(Unknown Source)
at java.util.Vector$Itr.next(Unknown Source)
at com.bjsxt.base.coll012.Tickets.main(Tickets.java:27)

jdk1.5之后提供了多种并发容器来代替同步类容器从而改善性能。同步容器的状态都是串行化的。他们虽然实现了线程安全,

但是严重降低了并发性,在多线程环境时,严重减低了应用程序的吞吐量。

并发类容器是专门针对并发设计的,使用ConcurrentHashMap来代替给予散列的传统的HashTable

,并且在ConcurrentHashMap中,添加了一些常见符合操作的支持。以及使用了CopyOnWriteArrayList代替Voctor

,并发的CopyonWriteArraySet,以及并发的Queue,ConcurrentLinkedQueue和LinkedBlockQueue,前者是高性能的对列,后者是以阻塞形式的

对列,具体实现queue还有很多,例如ArrayBlockingQueue,PriorityBlokjingQueue,SynchronousQueue等。

4.2  Copy-On-Write 容器

Copy-On-Write 简称COW,是一种用于程序设计中的优化策略。

JDK中的COW容器有两种:CopyOnWriteArrayList和CopyOnWriteArraySet,

什么是CopyOnWrite容器呢?

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器中添加元素的时候,不直接往当前的容器添加

而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用

指向新的容器,这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素

所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

public class UseCopyOnWrite {

    public static void main(String[] args) {
        
        CopyOnWriteArrayList<String> cwal = new CopyOnWriteArrayList<String>();
        CopyOnWriteArraySet<String> cwas = new CopyOnWriteArraySet<String>();
        
        
    }
}
ConcurrentMap的使用
public class UseConcurrentMap {

    public static void main(String[] args) {
        ConcurrentHashMap<String, Object> chm = new ConcurrentHashMap<String, Object>();
        chm.put("k1", "v1");
        chm.put("k2", "v2");
        chm.put("k3", "v3");
        chm.putIfAbsent("k4", "vvvv");
        //System.out.println(chm.get("k2"));
        //System.out.println(chm.size());
        
        for(Map.Entry<String, Object> me : chm.entrySet()){
            System.out.println("key:" + me.getKey() + ",value:" + me.getValue());
        }
        
    }
}

5.1 并发Queue

在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能对列,一个是以BlockingQueue接口为代表的阻塞对列,那种都继承自Queue

5.2 ConcurrentLinkedQueue

     ConcurrentLinkedQueue:一种适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlokingQueue.它是一个基于连接节点的无界线程的安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不准null匀速。

  ConcurrentLinkedQueue重要方法,

       add()和offer()都是加入元素的方法(在ConcurrrentLinkedQueue中,这两个方法没有任何区别)

       poll()和peek()都是去头元素节点,区别在于前者会删除元素,后者不会。

5.3 BlokingQueue接口

     ArrayBlokingQueue:基于数组的阻塞对列实现,在ArrayBlokingQueue内部,维护了一个定长的数组,以便缓存对列 中的数据对象,其内部没实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者是先进后出,也叫有界对列,在很多场合非常适用。

   LinkedBlockingQueue:基于链表的阻塞队列,同ArrayBlockingQueue类似,器内部也维持着一个数据缓冲队列(该队列由一个链表构成),LinkedBlockingQueue之所以能够搞笑的处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者的完全并行运行,他是一个无界队列。

 PriorityBlockingQueue:基于优先级的阻塞队列(优先级的判断通过构造函数传入的compator对象来决定的,也就是说传入队列的对象必须实现Comparable接口),在实现PriroityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,它也是一个无界的队列。

DelayQueue:带有延迟时间的Queue,其中的元素只有当指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用的场景很多,比如对缓存超时的数据进行移除,

.任务超时处理,空闲连接的关闭等等。

 SynchronousQueue:一个没有缓冲的队列,生产者生产的数据直接会被消费者获取并消费。

下面是分别得例子

public class UseDeque {

	public static void main(String[] args) {
		
		LinkedBlockingDeque<String> dq = new LinkedBlockingDeque<String>(10);
		dq.addFirst("a");
		dq.addFirst("b");
		dq.addFirst("c");
		dq.addFirst("d");
		dq.addFirst("e");
		dq.addLast("f");
		dq.addLast("g");
		dq.addLast("h");
		dq.addLast("i");
		dq.addLast("j");
		//dq.offerFirst("k");
		System.out.println("查看头元素:" + dq.peekFirst());
		System.out.println("获取尾元素:" + dq.pollLast());
		Object [] objs = dq.toArray();
		for (int i = 0; i < objs.length; i++) {
			System.out.println(objs[i]);
		}
		
	}
}
public class UsePriorityBlockingQueue {

	
	public static void main(String[] args) throws Exception{
		
		
		PriorityBlockingQueue<Task> q = new PriorityBlockingQueue<Task>();
		
		Task t1 = new Task();
		t1.setId(3);
		t1.setName("id为3");
		Task t2 = new Task();
		t2.setId(4);
		t2.setName("id为4");
		Task t3 = new Task();
		t3.setId(1);
		t3.setName("id为1");
		
		//return this.id > task.id ? 1 : 0;
		q.add(t1);	//3
		q.add(t2);	//4
		q.add(t3);  //1
		
		// 1 3 4
		System.out.println("容器:" + q);
		System.out.println(q.take().getId());
		System.out.println("容器:" + q);
//		System.out.println(q.take().getId());
//		System.out.println(q.take().getId());
		

		
	}
}
public class Wangmin implements Delayed {  
    
    private String name;  
    //身份证  
    private String id;  
    //截止时间  
    private long endTime;  
    //定义时间工具类
    private TimeUnit timeUnit = TimeUnit.SECONDS;
      
    public Wangmin(String name,String id,long endTime){  
        this.name=name;  
        this.id=id;  
        this.endTime = endTime;  
    }  
      
    public String getName(){  
        return this.name;  
    }  
      
    public String getId(){  
        return this.id;  
    }  
      
    /** 
     * 用来判断是否到了截止时间 
     */  
    @Override  
    public long getDelay(TimeUnit unit) { 
        //return unit.convert(endTime, TimeUnit.MILLISECONDS) - unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    	return endTime - System.currentTimeMillis();
    }  
  
    /** 
     * 相互批较排序用 
     */  
    @Override  
    public int compareTo(Delayed delayed) {  
    	Wangmin w = (Wangmin)delayed;  
        return this.getDelay(this.timeUnit) - w.getDelay(this.timeUnit) > 0 ? 1:0;  
    }  
    
}  
public class WangBa implements Runnable {  
    
    private DelayQueue<Wangmin> queue = new DelayQueue<Wangmin>();  
    
    public boolean yinye =true;  
      
    public void shangji(String name,String id,int money){  
        Wangmin man = new Wangmin(name, id, 1000 * money + System.currentTimeMillis());  
        System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"交钱"+money+"块,开始上机...");  
        this.queue.add(man);  
    }  
      
    public void xiaji(Wangmin man){  
        System.out.println("网名"+man.getName()+" 身份证"+man.getId()+"时间到下机...");  
    }  
  
    @Override  
    public void run() {  
        while(yinye){  
            try {  
                Wangmin man = queue.take();  
                xiaji(man);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    public static void main(String args[]){  
        try{  
            System.out.println("网吧开始营业");  
            WangBa siyu = new WangBa();  
            Thread shangwang = new Thread(siyu);  
            shangwang.start();  
              
            siyu.shangji("路人甲", "123", 1);  
            siyu.shangji("路人乙", "234", 10);  
            siyu.shangji("路人丙", "345", 5);  
        }  
        catch(Exception e){  
            e.printStackTrace();
        }  
  
    }  
}  

运行结果:
       网吧开始营业
网名路人甲 身份证123交钱1块,开始上机...
网名路人乙 身份证234交钱10块,开始上机...
网名路人丙 身份证345交钱5块,开始上机...
网名路人甲 身份证123时间到下机...
网名路人丙 身份证345时间到下机...
网名路人乙 身份证234时间到下机...

6.1 多线程的设计模式

       并行设计模式属于设计优化的一部分,它是对一些常用的多线程结构的总结和抽象,与串行程序相比,并行程序的结构更为复杂,一次合理的并行设计面膜是在多线程开发中更具有意义,在这里主要介绍Future.master-Worker和生产者-消费者模式。

6.2 Future设计模式

     Future设计模式有点类似于商品订单,比如在网购是,当看中某一种商品的时候,就可以提交订单,在订单完成后,在贾丽丽等待商品送货上门即可。或者说更形象的我们发送Ajax请求的时候,页面是异步的进行后台处理,用户无需等待请求的结果,可以继续浏览或操作其他内容。

例子:

   

public interface Data {

	String getRequest();

}


public class FutureClient {

	public Data request(final String queryStr){
		//1 我想要一个代理对象(Data接口的实现类)先返回给发送请求的客户端,告诉他请求已经接收到,可以做其他的事情
		final FutureData futureData = new FutureData();
		//2 启动一个新的线程,去加载真实的数据,传递给这个代理对象
		new Thread(new Runnable() {
			@Override
			public void run() {
				//3 这个新的线程可以去慢慢的加载真实对象,然后传递给代理对象
				RealData realData = new RealData(queryStr);
				futureData.setRealData(realData);
			}
		}).start();
		
		return futureData;
	}
	
}


public class FutureData implements Data{

	private RealData realData ;
	
	private boolean isReady = false;
	
	public synchronized void setRealData(RealData realData) {
		//如果已经装载完毕了,就直接返回
		if(isReady){
			return;
		}
		//如果没装载,进行装载真实对象
		this.realData = realData;
		isReady = true;
		//进行通知
		notify();
	}
	
	@Override
	public synchronized String getRequest() {
		//如果没装载好 程序就一直处于阻塞状态
		while(!isReady){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//装载好直接获取数据即可
		return this.realData.getRequest();
	}


}

public class RealData implements Data{

	private String result ;
	
	public RealData (String queryStr){
		System.out.println("根据" + queryStr + "进行查询,这是一个很耗时的操作..");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("操作完毕,获取结果");
		result = "查询结果";
	}
	
	@Override
	public String getRequest() {
		return result;
	}

}


public class Main {

	public static void main(String[] args) throws InterruptedException {
		
		FutureClient fc = new FutureClient();
		Data data = fc.request("请求参数");
		System.out.println("请求发送成功!");
		System.out.println("做其他的事情...");
		
		String result = data.getRequest();
		System.out.println(result);
		
	}
}
运行结果:
      请求发送成功!
      做其他的事情...
      根据请求参数进行查询,这是一个很耗时的操作..
     操作完毕,获取结果
     查询结果



6.3 Mastter-Worker模式

       Master-Worker模式是常用的并行模式,它的核心思想是系统由两类进程协作工作:Maste进程和Worker进程。Master负责接收和分配任务,Worker负责处理子任务。当各个Worker子进程处理完成后,会将结果返回给Master,由Master做归纳和总结。

其好处是讲一个大任务分解成若干个小任务,并行执行,从而提高系统的吞吐量。

例子:

public class Task {

	private int id;
	private int price ;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	} 
	
}
public class Master {

	//1 有一个盛放任务的容器
	private ConcurrentLinkedQueue<Task> workQueue = new ConcurrentLinkedQueue<Task>();
	
	//2 需要有一个盛放worker的集合
	private HashMap<String, Thread> workers = new HashMap<String, Thread>();
	
	//3 需要有一个盛放每一个worker执行任务的结果集合
	private ConcurrentHashMap<String, Object> resultMap = new ConcurrentHashMap<String, Object>();
	
	//4 构造方法
	public Master(Worker worker , int workerCount){
		worker.setWorkQueue(this.workQueue);
		worker.setResultMap(this.resultMap);
		
		for(int i = 0; i < workerCount; i ++){
			this.workers.put(Integer.toString(i), new Thread(worker));
		}
		
	}
	
	//5 需要一个提交任务的方法
	public void submit(Task task){
		this.workQueue.add(task);
	}
	
	//6 需要有一个执行的方法,启动所有的worker方法去执行任务
	public void execute(){
		for(Map.Entry<String, Thread> me : workers.entrySet()){
			me.getValue().start();
		}
	}

	//7 判断是否运行结束的方法
	public boolean isComplete() {
		for(Map.Entry<String, Thread> me : workers.entrySet()){
			if(me.getValue().getState() != Thread.State.TERMINATED){
				return false;
			}
		}		
		return true;
	}

	//8 计算结果方法
	public int getResult() {
		int priceResult = 0;
		for(Map.Entry<String, Object> me : resultMap.entrySet()){
			priceResult += (Integer)me.getValue();
		}
		return priceResult;
	}
	

public class Worker implements Runnable {

	private ConcurrentLinkedQueue<Task> workQueue;
	private ConcurrentHashMap<String, Object> resultMap;
	
	public void setWorkQueue(ConcurrentLinkedQueue<Task> workQueue) {
		this.workQueue = workQueue;
	}

	public void setResultMap(ConcurrentHashMap<String, Object> resultMap) {
		this.resultMap = resultMap;
	}
	
	@Override
	public void run() {
		while(true){
			Task input = this.workQueue.poll();
			if(input == null) break;
			Object output = handle(input);
			this.resultMap.put(Integer.toString(input.getId()), output);
		}
	}

	private Object handle(Task input) {
		Object output = null;
		try {
			//处理任务的耗时。。 比如说进行操作数据库。。。
			Thread.sleep(500);
			output = input.getPrice();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return output;
	}



}

public class Main {

	public static void main(String[] args) {
		
		Master master = new Master(new Worker(), 20);
		
		Random r = new Random();
		for(int i = 1; i <= 100; i++){
			Task t = new Task();
			t.setId(i);
			t.setPrice(r.nextInt(1000));
			master.submit(t);
		}
		master.execute();
		long start = System.currentTimeMillis();
		
		while(true){
			if(master.isComplete()){
				long end = System.currentTimeMillis() - start;
				int priceResult = master.getResult();
				System.out.println("最终结果:" + priceResult + ", 执行时间:" + end);
				break;
			}
		}
		
	}
}

运行结果:最终结果:48252, 执行时间:2569

6.4 生产者和消费者设计模式

    生产者和消费者也是一个非常经典的多线程模式,我们在实际开发中应用非常广泛的思想理念,在该模式中:通常由两类线程,及若干个生产者的线程和若干个消费者的线程。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务,在生产者和消费者之间通过共享内存缓存区进行通信。

例子:


public final class Data {

	private String id;
	private String name;
	
	public Data(String id, String name){
		this.id = id;
		this.name = name;
	}
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString(){
		return "{id: " + id + ", name: " + name + "}";
	}
	
}

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Provider implements Runnable{
	
	//共享缓存区
	private BlockingQueue<Data> queue;
	//多线程间是否启动变量,有强制从主内存中刷新的功能。即时返回线程的状态
	private volatile boolean isRunning = true;
	//id生成器
	private static AtomicInteger count = new AtomicInteger();
	//随机对象
	private static Random r = new Random(); 
	
	public Provider(BlockingQueue queue){
		this.queue = queue;
	}

	@Override
	public void run() {
		while(isRunning){
			try {
				//随机休眠0 - 1000 毫秒 表示获取数据(产生数据的耗时) 
				Thread.sleep(r.nextInt(1000));
				//获取的数据进行累计...
				int id = count.incrementAndGet();
				//比如通过一个getData方法获取了
				Data data = new Data(Integer.toString(id), "数据" + id);
				System.out.println("当前线程:" + Thread.currentThread().getName() + ", 获取了数据,id为:" + id + ", 进行装载到公共缓冲区中...");
				if(!this.queue.offer(data, 2, TimeUnit.SECONDS)){
					System.out.println("提交缓冲区数据失败....");
					//do something... 比如重新提交
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void stop(){
		this.isRunning = false;
	}
	
}


import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class Consumer implements Runnable{

	private BlockingQueue<Data> queue;
	
	public Consumer(BlockingQueue queue){
		this.queue = queue;
	}
	
	//随机对象
	private static Random r = new Random(); 

	@Override
	public void run() {
		while(true){
			try {
				//获取数据
				Data data = this.queue.take();
				//进行数据处理。休眠0 - 1000毫秒模拟耗时
				Thread.sleep(r.nextInt(1000));
				System.out.println("当前消费线程:" + Thread.currentThread().getName() + ", 消费成功,消费数据为id: " + data.getId());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

public class Main {

	public static void main(String[] args) throws Exception {
		//内存缓冲区
		BlockingQueue<Data> queue = new LinkedBlockingQueue<Data>(10);
		//生产者
		Provider p1 = new Provider(queue);
		
		Provider p2 = new Provider(queue);
		Provider p3 = new Provider(queue);
		//消费者
		Consumer c1 = new Consumer(queue);
		Consumer c2 = new Consumer(queue);
		Consumer c3 = new Consumer(queue);
		//创建线程池运行,这是一个缓存的线程池,可以创建无穷大的线程,没有任务的时候不创建线程。空闲线程存活时间为60s(默认值)

		ExecutorService cachePool = Executors.newCachedThreadPool();
		cachePool.execute(p1);
		cachePool.execute(p2);
		cachePool.execute(p3);
		cachePool.execute(c1);
		cachePool.execute(c2);
		cachePool.execute(c3);

		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		p1.stop();
		p2.stop();
		p3.stop();
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
//		cachePool.shutdown(); 
//		cachePool.shutdownNow();
		

	}
	
}


运行结果:
当前线程:pool-1-thread-1, 获取了数据,id为:1, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-3, 获取了数据,id为:2, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-2, 获取了数据,id为:3, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-3, 获取了数据,id为:4, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-1, 获取了数据,id为:5, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 2
当前线程:pool-1-thread-2, 获取了数据,id为:6, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 4
当前线程:pool-1-thread-1, 获取了数据,id为:7, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 5
当前线程:pool-1-thread-3, 获取了数据,id为:8, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 3
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 1
当前线程:pool-1-thread-2, 获取了数据,id为:9, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-3, 获取了数据,id为:10, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 6
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 9
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 10
当前线程:pool-1-thread-3, 获取了数据,id为:11, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-1, 获取了数据,id为:12, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 8
当前线程:pool-1-thread-3, 获取了数据,id为:13, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 7
当前线程:pool-1-thread-1, 获取了数据,id为:14, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 12
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 14
当前线程:pool-1-thread-3, 获取了数据,id为:15, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-2, 获取了数据,id为:16, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 13
当前线程:pool-1-thread-2, 获取了数据,id为:17, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-2, 获取了数据,id为:18, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-1, 获取了数据,id为:19, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-2, 获取了数据,id为:20, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 11
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 16
当前线程:pool-1-thread-3, 获取了数据,id为:21, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 15
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 18
当前线程:pool-1-thread-1, 获取了数据,id为:22, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 19
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 17
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 20
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 21
当前线程:pool-1-thread-2, 获取了数据,id为:23, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-3, 获取了数据,id为:24, 进行装载到公共缓冲区中...
当前线程:pool-1-thread-1, 获取了数据,id为:25, 进行装载到公共缓冲区中...
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 22
当前消费线程:pool-1-thread-6, 消费成功,消费数据为id: 23
当前消费线程:pool-1-thread-5, 消费成功,消费数据为id: 25
当前消费线程:pool-1-thread-4, 消费成功,消费数据为id: 24


7.1  Executor 框架

     为了更好地进行线程控制,JDK提供了一套线程框架Executor 帮助开发人员有效的进行线程控制,它们都在java.util.Concurrent包中,是JDK并发包的核心,其中有一个比较重要的类,Execuors,他扮演这线程工厂的角色,我们通过Executor可以常见特定功能的线程池。

  Executors创建线程池的方法:

              newFixedThreadPool()方法,该方法返回固定数量的线程池,该方法的线程数量始终不变,当有一个任务提交的时候,若线程池中空闲,则立即执行,若没有,则会被暂缓在 一个任务队列中等待有空闲的线程去执行。

              newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任队列中。

              newCachedThreadPool()方法,返回一个可以根据实际情况调整线程个数的线程池,不限最大线程数量,若有空闲的线程则执行任务,若无任务则不创建线程,并且每一个空闲线程在60秒后hi自动回收。

            new ShceduledThreadPool方法,该方法返回一个SchededExceutorService对象,但是该线程池可以指定线程的数量。

 7.2      自定义线程池

            若Excutors工厂类无法满足我们的需求,可以自己去创建自定义的线程池。其实Executor工厂类里面的创建线程方法器内部均是用了ThreadPoolExecutor这个类,这个类可以自定义线程,构造的方法如下。

           public ThreadPoolExecutor(

             int corePoolSize,

             int maximumPoolSize,

             long keepAliveTime,

             BlokingQueue<Runnable> workQueue,

           ThreadFactory threadFactory,

            RejectedExceutionHandler handler){...}

代码  

public class UseExecutors {

	public static void main(String[] args) {
		
		//ExecutorService pool = Executors.newSingleThreadExecutor()
		
		//cache fixed single
		
		
		
	}
}
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

class Temp extends Thread {
    public void run() {
        System.out.println("run");
    }
}

public class ScheduledJob {
	
    public static void main(String args[]) throws Exception {
    
    	Temp command = new Temp();
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        
        ScheduledFuture<?> scheduleTask = scheduler.scheduleWithFixedDelay(command, 5, 1, TimeUnit.SECONDS);
    
    }
}

结果 
  run
run
run
run

7.3   自定义线程池使用详细

       这个方法对于队列是什么类型比较关键:

        在使用有界队列的时候,若有新的任务需要执行的时候,如果线程池实际线程数小于corePoolSize,则优先创建线程,若大于线程数,则会将任务加入队列,若队列已满,则在总线程数不大于maximuPoolSize的前提下,创建新的线程,若线程数大于maximumPoolSize,则拒绝执行策略。或其他自定义的方式。

       无界的任务队列是:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在的任务入队失败的情况。当有新任务到拉力,系统的线程数小于corePoolSize是,则新建线程执行任务,当达到corePoolSize后,就不会继续增加,若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的苏丽差异很大,无界队列会保持快速增长,知道耗尽系统内存。

JDK拒绝策略:

                     AborPolicy:直接抛出异常组织系统正常工作。

                    CallerRunsPolicy:只要线程未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。

                  DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务。

                 DiscardPolicy:丢弃无法处理的任务,不给于任务处理。

 有界队列例子:

        

public class MyTask implements Runnable {

	private int taskId;
	private String taskName;
	
	public MyTask(int taskId, String taskName){
		this.taskId = taskId;
		this.taskName = taskName;
	}
	
	public int getTaskId() {
		return taskId;
	}

	public void setTaskId(int taskId) {
		this.taskId = taskId;
	}

	public String getTaskName() {
		return taskName;
	}

	public void setTaskName(String taskName) {
		this.taskName = taskName;
	}

	@Override
	public void run() {
		try {
			System.out.println("run taskId =" + this.taskId);
			Thread.sleep(5*1000);
			//System.out.println("end taskId =" + this.taskId);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
	
	public String toString(){
		return Integer.toString(this.taskId);
	}

}


import java.net.HttpURLConnection;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class MyRejected implements RejectedExecutionHandler{

	
	public MyRejected(){
	}
	
	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		System.out.println("自定义处理..");
		System.out.println("当前被拒绝任务为:" + r.toString());
		

	}

}

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;



public class UseThreadPoolExecutor1 {


	public static void main(String[] args) {
		/**
		 * 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
		 * 若大于corePoolSize,则会将任务加入队列,
		 * 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
		 * 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
		 * 
		 */	
		ThreadPoolExecutor pool = new ThreadPoolExecutor(
				1, 				//coreSize
				2, 				//MaxSize
				60, 			//60
				TimeUnit.SECONDS, 
				new ArrayBlockingQueue<Runnable>(3)			//指定一种队列 (有界队列)
				//new LinkedBlockingQueue<Runnable>()
				//, new MyRejected()
				//, new DiscardOldestPolicy()
				);
		
		MyTask mt1 = new MyTask(1, "任务1");
		MyTask mt2 = new MyTask(2, "任务2");
		MyTask mt3 = new MyTask(3, "任务3");
		MyTask mt4 = new MyTask(4, "任务4");
		MyTask mt5 = new MyTask(5, "任务5");
		MyTask mt6 = new MyTask(6, "任务6");
		
		pool.execute(mt1);
        // 运行结果 
         //run taskId =1

		pool.execute(mt2);
         /*
           运行结果:
            run taskId =1
           run taskId =2
        */
		pool.execute(mt3);
         /*
          运行结果: 
        */
          /*
                  run taskId =1
                  run taskId =2
               run taskId =3
 
          */
		pool.execute(mt4);
          /*
             运行结果:
                 run taskId =1
                run taskId =2
                run taskId =3
                run taskId =4
          */
		pool.execute(mt5);
           /*
                运行结果: 
               run taskId =1
                run taskId =5
                run taskId =2
                run taskId =3
                run taskId =4
          */
		pool.execute(mt6);
		  /*
           运行结果:run taskId =1
run taskId =5
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task 6 rejected from java.util.concurrent.ThreadPoolExecutor@10dea4e[Running, pool size = 2, active threads = 2, queued tasks = 3, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
	at com.bjsxt.height.concurrent018.UseThreadPoolExecutor1.main(UseThreadPoolExecutor1.java:48)
          */
		pool.shutdown();
		
	}
}



无界队列:

          

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class UseThreadPoolExecutor2 implements Runnable{

	private static AtomicInteger count = new AtomicInteger(0);
	
	@Override
	public void run() {
		try {
			int temp = count.incrementAndGet();
			System.out.println("任务" + temp);
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws Exception{
		//System.out.println(Runtime.getRuntime().availableProcessors());
		BlockingQueue<Runnable> queue = 
				//new LinkedBlockingQueue<Runnable>();
				new ArrayBlockingQueue<Runnable>(10);
		ExecutorService executor  = new ThreadPoolExecutor(
					5, 		//core
					10, 	//max
					120L, 	//2fenzhong
					TimeUnit.SECONDS,
					queue);
		
		for(int i = 0 ; i < 20; i++){
			executor.execute(new UseThreadPoolExecutor2());
		}
		Thread.sleep(1000);
		System.out.println("queue size:" + queue.size());		//10
		Thread.sleep(2000);
	}


}

运行结果:
任务1
任务3
任务2
任务4
任务5
任务6
任务7
任务9
任务8
任务10
queue size:10
任务11
任务12
任务13
任务15
任务14
任务16
任务17
任务18
任务19
任务20


8.1   Concurrent.util常用工具类

          CyclicBarrier使用:

          假设有只有一个场景:每个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只有有一个人没有准备好,大家都在等待。

   例子:

           

import java.io.IOException;  
import java.util.Random;  
import java.util.concurrent.BrokenBarrierException;  
import java.util.concurrent.CyclicBarrier;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors; 
public class UseCyclicBarrier {

	static class Runner implements Runnable {  
	    private CyclicBarrier barrier;  
	    private String name;  
	    
	    public Runner(CyclicBarrier barrier, String name) {  
	        this.barrier = barrier;  
	        this.name = name;  
	    }  
	    @Override  
	    public void run() {  
	        try {  
	            Thread.sleep(1000 * (new Random()).nextInt(5));  
	            System.out.println(name + " 准备OK.");  
	            barrier.await();  
	        } catch (InterruptedException e) {  
	            e.printStackTrace();  
	        } catch (BrokenBarrierException e) {  
	            e.printStackTrace();  
	        }  
	        System.out.println(name + " Go!!");  
	    }  
	} 
	
    public static void main(String[] args) throws IOException, InterruptedException {  
        CyclicBarrier barrier = new CyclicBarrier(3);  // 3 
        ExecutorService executor = Executors.newFixedThreadPool(3);  
        
        executor.submit(new Thread(new Runner(barrier, "zhangsan")));  
        executor.submit(new Thread(new Runner(barrier, "lisi")));  
        executor.submit(new Thread(new Runner(barrier, "wangwu")));  
  
        executor.shutdown();  
    }  
  
}  

运行结果:
     lisi 准备OK.
wangwu 准备OK.
zhangsan 准备OK.
zhangsan Go!!
lisi Go!!
wangwu Go!!

       CountDownLacth使用:

              它经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作。

     

      例子:

      

import java.util.concurrent.CountDownLatch;

public class UseCountDownLatch {

	public static void main(String[] args) {
		
		final CountDownLatch countDown = new CountDownLatch(2);
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("进入线程t1" + "等待其他线程处理完成...");
					countDown.await();
					System.out.println("t1线程继续执行...");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("t2线程进行初始化操作...");
					Thread.sleep(3000);
					System.out.println("t2线程初始化完毕,通知t1线程继续...");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("t3线程进行初始化操作...");
					Thread.sleep(4000);
					System.out.println("t3线程初始化完毕,通知t1线程继续...");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		t1.start();
		t2.start();
		t3.start();
		
		
		
	}
}


运行结果 :
进入线程t1等待其他线程处理完成...
t3线程进行初始化操作...
t2线程进行初始化操作...
t2线程初始化完毕,通知t1线程继续...
t3线程初始化完毕,通知t1线程继续...
t1线程继续执行...




 注意:CyclicBarrier和CountDownLacth的区别。

                CountDownLacth:一个线程等待,其他n个线程发出通知,线程执行。

                 CyclicBarrier多个线程,线程执行。

Callable和Future使用:

              就是Future模式,JDK给于我们一个实现的封装,使用非常简单,

              Future模式非常适合在处理很耗时的业务逻辑时进行使用,可以有效的减少系统的相应时间,

提高系统的吞吐量。

例子:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class UseFuture implements Callable<String>{
	private String para;
	
	public UseFuture(String para){
		this.para = para;
	}
	
	/**
	 * 这里是真实的业务逻辑,其执行可能很慢
	 */
	@Override
	public String call() throws Exception {
		//模拟执行耗时
		Thread.sleep(5000);
		String result = this.para + "处理完成";
		return result;
	}
	
	//主控制函数
	public static void main(String[] args) throws Exception {
		String queryStr = "query";
		//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类
		FutureTask<String> future = new FutureTask<String>(new UseFuture(queryStr));
		
		FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr));
		//创建一个固定线程的线程池且线程数为1,
		ExecutorService executor = Executors.newFixedThreadPool(2);
		//这里提交任务future,则开启线程执行RealData的call()方法执行
		//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
		
		Future f1 = executor.submit(future);		//单独启动一个线程去执行的
		Future f2 = executor.submit(future2);
		System.out.println("请求完毕");
		
		try {
			//这里可以做额外的数据操作,也就是主程序执行其他业务逻辑
			System.out.println("处理实际的业务逻辑...");
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待
		System.out.println("数据:" + future.get());
		System.out.println("数据:" + future2.get());
		
		executor.shutdown();
	}

}

运行结果:
请求完毕
处理实际的业务逻辑...
数据:query处理完成
数据:query处理完成




信号量的相关知识:

          在Semaphore信号量非常适合高并发访问,在新系统上线之前,要对系统的访问量进行评估,当然这个值肯定不是随便就能想出来的,是经过过往的经验,数据,历年的访问量,已经推广力度进行一个合理的评估,当然评估标准不能太大也不能太小,太大的话投入的资源达不到实际的效果,纯粹是浪费资源,太小的话,在某个时间点上一个高峰值访问量上来直接访问可以压垮系统。

      相关概念:

              PV(page view) 网站的总访问量,页面浏览量或者是点击量,用户没刷新一次就会被记录一次。

              UV(unique Visitor) 访问网站的一台电脑客户端为一个访客,一般来讲,时间上以z00:00-24:00之内相同ip客户端只是记录一次。

            QPS(query per second) 即每秒查询数,qps在很大程度上代表业务上的繁忙程度,每次请求的背后,可能对应着对此磁盘的IO,多次网络请求,多个CPU时间片等。我们通过qps可以非常直观地谅解当前系统业务情况,一旦当期那qps超过所设定的预警阙值可以考虑增加机器多集群扩容,以免压力过大导致宕机,可以根据前期的压力测试得到估值,在结合后期综合运维情况,估算出阙值。

          PT(Response time) 即请求相应时间,这个指标非常关键,直接说明前端用户的体验,一次任何系统设计师都想降低Rt时间。

        当然还涉及CPU,内存,网络,磁盘等情况,更细节的问题很多,如select.update,delete等数据库层面的统计。

信号量的例子:

          

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.Semaphore;  
  
public class UseSemaphore {  
  
    public static void main(String[] args) {  
        // 线程池  
        ExecutorService exec = Executors.newCachedThreadPool();  
        // 只能5个线程同时访问  
        final Semaphore semp = new Semaphore(5);  
        // 模拟20个客户端访问  
        for (int index = 0; index < 20; index++) {  
            final int NO = index;  
            Runnable run = new Runnable() {  
                public void run() {  
                    try {  
                        // 获取许可  
                        semp.acquire();  
                        System.out.println("Accessing: " + NO);  
                        //模拟实际业务逻辑
                        Thread.sleep((long) (Math.random() * 10000));  
                        // 访问完后,释放  
                        semp.release();  
                    } catch (InterruptedException e) {  
                    }  
                }  
            };  
            exec.execute(run);  
        } 
        
        try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
        
        //System.out.println(semp.getQueueLength());
        
        
        
        // 退出线程池  
        exec.shutdown();  
    }  
  
}  


运行结果:

Accessing: 2
Accessing: 0
Accessing: 1
Accessing: 5
Accessing: 3
15
Accessing: 4
Accessing: 6
Accessing: 7
Accessing: 8
Accessing: 9
Accessing: 10
Accessing: 11
Accessing: 12
Accessing: 13
Accessing: 14
Accessing: 15
Accessing: 16
Accessing: 17
Accessing: 18
Accessing: 19

容量评估:一般来说通过开发.运维.测试.以及业务等相关人员。综合处系统的一系列阀值,然后我们根据相关阀值如qps.tr等,对系统进行有效的变更。

 一般来讲,我们进行多轮测试之后,可以对系统的峰值进行评估,采用所谓的80/20原则,机80%的访问请求将在20%的时间内到达,这样我们可以根据系统对应的PV计算出峰值qps。

峰值qps = (总PV*80%)/(60*60*24*20%)

然后将总的峰值qps除以单台机器所能承受的最高的qps,就是所需要机器的数量:

 机器的数量=总的峰值qps/压测得出的单机极限qps

  当然不排除系统在上线前进行大型促销活动,或者双11,双12热点事件,遭受到DDos攻击等情况,系统的开发和运维需要了解到到当前系统运行状态和负载情况,一般都会有后台系统去维护。

  Semaphore可以控制系统的流量,拿到信号量的线程可以进入,通过acquire()和release()获取和释放访问许可。

9.1 锁

      在java多线程中,我们知道可以使用sunchronized关键字来实现线程间的同步互斥工作,其实实际上还有一个更优秀的机制去完成这个‘互斥同步’工作,它就是Lock对象,在这主要是有两种锁,重入锁和读写锁。他们既有比synchroniezd更为强大的功能,并且有嗅探锁定,多路分支等功能。

    9.2 ReentrantLock(重入锁)

      重入锁:在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁,不然会造成锁永远无法释放,其他线程永远进不来的结果。

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UseReentrantLock {
	
	private Lock lock = new ReentrantLock();
	
	public void method1(){
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
			Thread.sleep(1000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			
			lock.unlock();
		}
	}
	
	public void method2(){
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
			Thread.sleep(2000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {

		final UseReentrantLock ur = new UseReentrantLock();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				ur.method1();
				ur.method2();
			}
		}, "t1");

		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//System.out.println(ur.lock.getQueueLength());
	}
	
	
}
运行结果:
当前线程:t1进入method1..
当前线程:t1退出method1..
当前线程:t1进入method2..
当前线程:t1退出method2..




9.3 锁与等待/通知

       在使用synchronized的时候,如果需要多线程间进行协同工作则需要Object的wait()和notify(),notifyAll方法进行配合工作。

     同样,在使用Lock的时候,可以使用一个新的等待/通知的类,它就是Condition。这个condition一定是针对具体某一把锁的

。也就是在只有锁的基础上才会产生Condition.

例子:

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UseCondition {

	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void method1(){
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
			condition.await();	// Object wait
			System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void method2(){
		try {
			lock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
			condition.signal();		//Object notify
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		
		final UseCondition uc = new UseCondition();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				uc.method1();
			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				uc.method2();
			}
		}, "t2");
		t1.start();

		t2.start();
	}
	
	
	
}

运行结果:
当前线程:t1进入等待状态..
当前线程:t1释放锁..
当前线程:t2进入..
当前线程:t2发出唤醒..
当前线程:t1继续执行...


    

9.4  多Condition 

     我们可以通过一个Lock对象产生多个Condition进行多线程之间的交互,非常的灵活,可以使得部分需要花型的线程唤醒,

其他线程继续等待通知。

例子:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UseManyCondition {

	private ReentrantLock lock = new ReentrantLock();
	private Condition c1 = lock.newCondition();
	private Condition c2 = lock.newCondition();
	
	public void m1(){
		try {
			lock.lock();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");
			c1.await();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void m2(){
		try {
			lock.lock();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");
			c1.await();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void m3(){
		try {
			lock.lock();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");
			c2.await();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void m4(){
		try {
			lock.lock();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
			c1.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void m5(){
		try {
			lock.lock();
			System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
			c2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		
		
		final UseManyCondition umc = new UseManyCondition();
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m1();
			}
		},"t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m2();
			}
		},"t2");
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m3();
			}
		},"t3");
		Thread t4 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m4();
			}
		},"t4");
		Thread t5 = new Thread(new Runnable() {
			@Override
			public void run() {
				umc.m5();
			}
		},"t5");
		
		t1.start();	// c1
		t2.start();	// c1
		t3.start();	// c2
		

		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		t4.start();	// c1
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t5.start();	// c2
		
	}
	
	
	
}

输出结果:
当前线程:t1进入方法m1等待..
当前线程:t2进入方法m2等待..
当前线程:t3进入方法m3等待..
当前线程:t4唤醒..
当前线程:t1方法m1继续..
当前线程:t2方法m2继续..
当前线程:t5唤醒..
当前线程:t3方法m3继续..

   

9.5 Lock/Condition 其他方法和用法。

公平锁和非公平锁:

  Lock lock = new ReenTrantLock(boolean isFair);

  lock用法:

     tryLock();尝试获取锁,获得结果用true/false 返回。

     tryLock();在给定的时间内尝试获得锁,获得结果用true/false 返回。

      isFair():是否公平锁。

      isLocked:是否锁定。

      getHoldCount():查询当前线程保持此锁的个数,也就是调用lock次数。

      lockInterruptibly():优先相应中断的锁。

9.6 ReentranReadWriteLock(读写锁)

     读写锁ReentrantReadWriteLock,其核心就是实现读写分离的锁。在高并发访问写,尤其是读多谢少的情况下,性能要远远高于重入锁。之前的synchronized.ReentrantLock时,那么读写锁则不同,其本质是分成两个锁,分别是读锁,写锁,多个线程可以并发的进行访问,但是在写锁的时候,只能一个一个的顺序访问。

口诀:读读共享,写写互斥,读写互斥。


import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class UseReentrantReadWriteLock {

	private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
	private ReadLock readLock = rwLock.readLock();
	private WriteLock writeLock = rwLock.writeLock();
	
	public void read(){
		try {
			readLock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			readLock.unlock();
		}
	}
	
	public void write(){
		try {
			writeLock.lock();
			System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
			Thread.sleep(3000);
			System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			writeLock.unlock();
		}
	}
	
	public static void main(String[] args) {
		
		final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.read();
			}
		}, "t1");
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.read();
			}
		}, "t2");
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.write();
			}
		}, "t3");
		Thread t4 = new Thread(new Runnable() {
			@Override
			public void run() {
				urrw.write();
			}
		}, "t4");		
		
//		t1.start();
//		t2.start();
		
//		t1.start(); // R 
//		t3.start(); // W
		
		t3.start();
		t4.start();
		
	}
}

运行结果:
当前线程:t3进入...
当前线程:t3退出...
当前线程:t4进入...
当前线程:t4退出...

9.7 锁优化总结

 1.避免死锁。

 2.减少锁的持有时间。

3.减小锁的粒度。

4.多的分离。

5.尽量使用无锁的操作,如原子的操作(Atomic系列类),volatile关键字。

高并发的框架     disruptor

1.1 Disruptor并发框架简介

Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。 Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。 Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。 

1.2 Disruptor并发框架使用

目前我们使用disruptor已经更新到了3.x版本,比之前的2.x版本性能更加的优秀,提供更多的API使用方式。 下载disruptor-3.3.2.jar引入我们的项目既可以开始disruptor之旅。 在使用之前,首先说明disruptor主要功能加以说明,你可以理解为他是一种高效的"生产者-消费者"模型。也就性能远远高于传统的BlockingQueue容器。 官方学习网站:http://ifeve.com/disruptor-getting-started/

猜你喜欢

转载自blog.csdn.net/qq_36262421/article/details/83097596