【Java】多线程

多线程在我操作系统的博客中也有涉及,而这里我们讲一下java中的多线程实现。

先回顾一下,

进程是运行中的应用程序,一个应用程序可以同时启动多个进程。

同一个进程的线程是共享一个地址空间的。


抢占式:随时能够中断另一个任务。

非抢占式:只有一个任务同意被中断时才能被中断。会导致死锁。

多线程:共享变量因此便于通信,创建开销少。

多进程:相互独立,通信有共享内存和消息传递,创建开销很大。

java的GUI用一个单独的线程在后台收集用户界面的事件。

因此repaint调用时,会把这个事件发送到事件请求队列中,执行完前面的事件,才会执行repaint事件。

java有一个线程在后台垃圾回收。

Thread.sleep(int i);  线程睡眠i毫秒。


小知识点:多线程下载为何快?


多线程下载速度快是因为服务器规定为每个用户提供50K的速度,但是如果利用5个线程同时下载,则会提高到250K的速度;



java多线程实现方法:


一般我们使用实现Runnable方法,因为更加体现面向对象的思想;将多线程的业务逻辑封装成一个Runnable中;

实现Runnable接口,实现run方法


(1)继承Runnable并实现public void run();

(2)Thread t=new Thread(Runnable r);

(3)t.start();自动调用run方法,执行完就返回。

注意点:

(1)覆盖的run方法不能声明抛出异常,因为继承的方法不能抛出比父类更多的异常。

(2)不要随便覆写start方法,如果要覆写,则调用super.start().

(3)一个线程只能被启动一次。

(4)两个线程之间没有任何关系,一个线程抛出异常终止,另一个线程没有任何影响。

以下程序说明多个线程没有任何影响:

public class MyThread2{
	public static void main(String args[]){
		Thread t1 = new Thread(new Thread1());
		Thread t2 = new Thread(new Thread2());
		t1.start();
		t2.start();
	}
}
class Thread1 implements Runnable{
	public void run(){
		try{
			Thread.sleep(500);
		}
		catch(Exception e){}
		int a = 1/0;
		System.out.println("线程1结束");
	}
}
class Thread2 implements Runnable{
	public void run(){
		for(int a=0;a<20;a++){
			System.out.println("a = "+a );
			try{
				Thread.sleep(100);
			}
			catch(Exception e){}
		}
		System.out.println("线程2结束");
	}
}
//线程相互不影响


继承Thread方法,覆写run方法


runnable能够实现资源共享。


问题:如果一个线程同时实现Runnable接口和继承Thread类,则会调用哪一个?

答:会调用Thread类的方法;因为Thread类的run源码如下:

class Thread{
      private Runnable target; 
      public void run() {
	if (target != null) {//如果Runnable不为空,则调用runnable的方法;
	    target.run();
	}
    }
}

因此传统的实现多线程的继承Thread是重写run方法;

但是如果继承了Thread类并实现了run方法,则会覆盖此方法,因此如果调用run方法,则会调用Thread覆写的run方法;

以下代码会打印出Thread;

package org.xiazdong.thread;

import org.junit.Test;

public class ThreadDemo01 {

	@Test
	public void test1() {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Runnable");
			}

		}) {

			@Override
			public void run() {
				System.out.println("Thread");
			}

		};
		thread.start();
	}
}


常用方法:

(1)Thread cur = Thread.currentThread();

(2)String name = cur.getName();

(3)cur.setName(String name);


t.interrupt()请求中断一个线程,线程的中断状态位置位.当线程在阻塞时被请求中断,则抛出异常并被终止,特别在sleep()这个阻塞调用中,如果有请求中断,则会清除中断状态位。

Thread.currentThread().isInterrupted()判断中断状态位是否被置位。


线程状态:

1.new。Thread t=new Thread()时。

        在堆中创建一个线程对象,这个对象和一般的JAVA对象一样。

2.runnable。t.start()时。注意只是可运行的,因此可以运行也可以没有运行,因为在线程运行时会有中断发生。

        JVM创建了方法调用栈、程序计数器,这个线程放在可运行池中,等待CPU授权。

3.running.执行中。

4.blocked。当(1)sleep()(2)I/O中断(3)申请锁但是没有得到。当被阻塞时,CPU可以运行另外的线程。

5.dead。当(1)run正常返回(2)run由于异常返回。

        但是都不会对其他线程造成影响。

t.isAlive()判断是否是可运行的或阻塞的。

优先级:

线程调度器只是把优先级看作一个参考因素,而不是全部。

t.setPriority(int );

static yield()  当前线程让步,选择其他线程中优先级最高的。

t.setDaemon(boolean)   标记为守护线程。

守护线程:为其他线程提供服务,当只有守护线程时,则程序退出。

后台线程特性:
(1)如果前台线程全部结束,则JVM会终止后台线程。

public class MyThread5 extends Thread{
	private static int a = 0;
	public void run(){
		while(true){
			System.out.println(getName()+":"+a++);
			try{
				Thread.sleep(100);
			}
			catch(Exception e){}
			if(a==100) break;
			yield();
		}
	}
	public static void main(String args[]){
		MyThread5 t1 = new MyThread5();
		MyDaemon t2 = new MyDaemon();
		t2.setDaemon(true);
		t1.start();
		t2.start();
	}
	static class MyDaemon extends Thread{
		public void run(){
			while(true){
				a = 1;
				try{
				Thread.sleep(1000);
			}
			catch(Exception e){}
			}
		}
	}
}


Timer和TimerTask(定时器和定时任务)


Timer timer = new Timer(boolean daemon);    创建一个定时器,参数是是否为后台线程。

TimerTask是一个抽象类,必须继承并覆写run方法。

timer.schedule(task,long delay,long period);    delay 毫秒是运行task的延迟,period 毫秒是运行task的周期,即每隔perioid执行一次。

timer.schedule(TimerTask task, Date time) ; 在特定的时间执行这个定时器任务;


package timer;
import java.util.*;
public class TimerDemo extends Thread{
	private int a;
	private static int count;
	public void start(){
		super.start();
		Timer timer = new Timer(true);
		TimerTask task = new TimerTask(){
			public void run(){
				while(true){
					a = 0;
					try{
						sleep(1000);
					}
					catch(Exception e){}
				}
			}
		};
		timer.schedule(task,10,500);
	}
	public void run(){
		while(true){
			System.out.println(getName()+":"+a++);
			try{
				Thread.sleep(100);
			}catch(Exception e){}
			if(count++==100) break;
		}
	}
	public static void main(String args[]){
		TimerDemo t1 = new TimerDemo();
		t1.start();
	}
}



任务:2秒运行一次,4秒运行一次,再2秒运行一次.....(连环任务)

通过以下代码能够实现循环调用定时器;

class MyTask extends TimerTask{
public void run(){
	new Timer().schedule(new MyTask(),2000);
	}
}
package org.xiazdong.thread;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class ThreadDemo01 {

	public static void main(String[] args) throws Exception {

		Timer timer = new Timer();
		timer.schedule(new MyTask(), 2000);
		while (true) {
			System.out.println(new Date().getSeconds());
			Thread.sleep(1000);
		}
	}
}
class MyTask extends TimerTask{
	public static int count = 0;
	@Override
	public void run() {
		count = (count+1)%2;
		System.out.println("bombing");
		Timer t2 = new Timer();
		t2.schedule(new MyTask(), 2000+count*2000);
	}
}

线程调度


(1)设置优先级.

(2)sleep(); 当前线程阻塞,睡眠好后,则回到可运行状态,等待CPU。

(3)yield();让步,即把当前线程放回可运行池中,并选取一个优先级最高的获得CPU使用权。

(4)join();如果A线程是当前运行线程,则A调用B的join方法,则A进入阻塞状态,直到B执行完毕,A才恢复可运行。

public class MyThread3 extends Thread{
	private static StringBuffer buf = new StringBuffer();
	public void run(){
		for(int a=0;a<10;a++){
			buf.append("当前线程:"+currentThread().getName()+"\n");
		}
	}
	public static void main(String args[])throws Exception{
		MyThread3 t1 = new MyThread3();
		MyThread3 t2 = new MyThread3();
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(MyThread3.buf);
		

	}
}


sleep和yield的区别:


sleep:把机会留给其他线程,即使其他线程比当前睡眠的线程优先级低也不要紧。

yield:只会给相同优先级的线程给予机会,yield是直接进入可运行状态。而sleep则是先进入阻塞状态。

sleep比yield具有更好的移植性。


线程组:ThreadGroup


根据功能进行分类,比如有多个线程是用来下载图片的,当用户请求中断时,这些线程是一起中断的,为了操作方便,可以把他们归在一个线程组。

如果线程A创建了线程B,并且线程B在构造方法中没有指定线程组,则B加入A所属的线程组。

不能中途改变线程组。

默认有一个main线程组。

ThreadGroup g=new ThreadGroup("....");

Thread t1=new Thread(g,"...");

Thread t2=new Thread(g,"...");

g.interrupt();             中断t1和t2

g.activeCount();        g这个线程组中可运行状态的线程数量

t1.getThreadGroup();返回t1所属的线程组。

g.enumerate(Thread[] threads);        将当前还活着的线程的引用复制到threads中。


 未捕获异常处理器:

当在run方法发生异常时,如果你try-catch了,则会捕获了,如果你throws了,则会抛出,但是如果发生了那些你没有预料到的异常,则会发送给未捕获异常处理器,然后线程终止。

(1)Thread.UncaughtExceptionHandler接口是未捕获异常处理器,必须实现uncaughtException(Thread,Throwable);

(2)------设置:t.setUncaughtExceptionHandler(handler)

        -------设置默认:调用静态方法  Thread.setDefaultUncaughtExceptionHandler(handler);

        -------如果没有为线程设置未捕获异常处理器,则默认为ThreadGroup的未捕获异常处理器,因为ThreadGroup实现了这个接口。

class MachineGroup extends ThreadGroup{
	public MachineGroup(){
		super("MachineGroup");
	}
	public void uncaughtException(Thread t,Throwable e){
		System.out.println(getName()+" catches an exception from "+ t.getName());
		super.uncaughtException(t,e);
	}
}
class MachineHandler implements Thread.UncaughtExceptionHandler{
	private String name;
	public MachineHandler(String name){
		this.name = name;
	}
	public void uncaughtException(Thread t,Throwable e){
		System.out.println(name+" catches an exception from "+ t.getName());
	}
}
public class UncaughtDemo extends Thread{
	public UncaughtDemo(ThreadGroup group,String name){
		super(group,name);
	}
	public void run(){
		int a = 1/0;
	}
	public static void main(String args[]){
		ThreadGroup g = new MachineGroup();
		UncaughtExceptionHandler defaultHandler = new MachineHandler("DefaultHandler");
		UncaughtDemo.setDefaultUncaughtExceptionHandler(defaultHandler);
		UncaughtDemo m1 = new UncaughtDemo(g,"线程-1");
		UncaughtDemo m2 = new UncaughtDemo(g,"线程-2");
		UncaughtExceptionHandler curHandler = new MachineHandler("Machine2-Handler");
		m2.setUncaughtExceptionHandler(curHandler);
		m1.start();
		m2.start();
	}
}



同步 


在同步块中,如果执行Thread.sleep()或Thread.yield(),并不会释放锁,而是把执行的权利给了其他线程。

synchronized声明不会被继承。

建议:

为了尽可能地并发,我们应该只把对资源竞争的代码同步。


注意:同步只用于业务逻辑方法中,而不写在线程中!


wait()、notify()

每个对象都有一把锁,这个锁对应有一个等待队列,一个对象还有一个等待池(调用wait后的效果)

s.wait():当前线程释放s对象的锁,并且进入该对象的等待池。     一般放在while中。

s.notify():当前线程调用s的notify,把在s等待池中的一个线程放入锁队列。

while(flag){
	t.wait();
}
.....
t.notify();

























虚假唤醒


如果代码形式为:
if(flag){
t.wait();
}
则会出现虚假唤醒的问题;

问题1:一个类的普通方法怎么互斥?

通过在方法声明中使用synchronized;


问题2:一个类的普通方法和静态方法怎么互斥?

通过用类的class对象进行同步!而如果静态方法声明synchronized,则默认就是相对class对象同步;

比如:

//fun和staticfun不能同步
//fun2和staticfun能够同步
class A{
	//以下的synchronized 表示 synchronized(this)
	public synchronized void fun(){
		
	}
	//以下的synchronized 表示 synchronized(A.class)
	public synchronized static void staticfun(){
		
	}
	public void fun2(){
		synchronized(A.class){
			
		}
	}
}



    
    

共享对象种类:

(1)线程范围内共享数据:用ThreadLocal; (2)每个线程都共享数据:通过Runnable构造函数传递 或者 static属性;
Runnable run1 = new MyRunnable1(data);
class MyRunnable1 implements Runnable{
	private Data1 data;
	public MyRunnable1(Data1 data){
		this.data = data;
	}
}

private static Data1 data2 = new Data1();
public static void main(String[] args) {
	new Thread(){
		public void run(){
                      //data2操纵数据
		}
	}.start();
}



以下代码通过传递Runnable构造函数操纵同一个j;
package org.xiazdong.sync;

public class Demo05 {

	
	public static void main(String[] args) {
		Data1 data = new Data1();
		Runnable run1 = new MyRunnable1(data);
		Runnable run2 = new MyRunnable2(data);
		new Thread(run1).start();
		new Thread(run2).start();
	}

}
class MyRunnable1 implements Runnable{

	private Data1 data;
	public MyRunnable1(Data1 data){
		this.data = data;
	}
	@Override
	public void run() {
		data.increment();
		System.out.println(data.j);
	}
}
class MyRunnable2 implements Runnable{

	private Data1 data;
	public MyRunnable2(Data1 data){
		this.data = data;
	}
	@Override
	public void run() {
		data.decrement();
		System.out.println(data.j);
	}
}
class Data1{
	int j = 100;
	public synchronized void increment(){
		j++;
	}
	public synchronized void decrement(){
		j--;
	}
}

以下代码通过static属性在线程间操纵同一个j;
package org.xiazdong.sync;

public class Demo05 {

	private static Data1 data2 = new Data1();
	public static void main(String[] args) {

		new Thread(){
			public void run(){
				data2.increment();
				System.out.println(data2.j);
			}
		}.start();
		new Thread(){
			public void run(){
				data2.decrement();
				System.out.println(data2.j);
			}
		}.start();
	}
}
class Data1{
	int j = 100;
	public synchronized void increment(){
		j++;
	}
	public synchronized void decrement(){
		j--;
	}
}


同步块:


synchronized(obj){    critical section}  即利用对象的锁进行同步。

实例


子线程执行10次,再主线程执行100次,再子线程执行10次,再主线程执行100次.......

package org.xiazdong.sync;

public class Demo02 {

	public static void main(String[] args) throws InterruptedException {
		final Business business = new Business();
		new Thread(){
			public void run(){
				while(true)
				business.sub();
			}
			
		}.start();
		Thread.sleep(10);
		new Thread(){
			public void run(){
				while(true)
				business.main();
			}
		}.start();
	}
}

class Business {
	boolean subfinished = false;	//记录是否该sub()执行
	public synchronized void sub(){
		if(!subfinished){
			subfinished = true;
			for(int i=0;i<10;i++){
				System.out.println("sub:"+(i+1));
			}
		}
	}
	public synchronized void main(){
		if(subfinished){
			subfinished = false;
			for(int i=0;i<100;i++){
				System.out.println("main:"+(i+1));
			}
		}
	}
}


生产者-消费者问题
public class AppDemo{
	public static void main(String args[]){
		Stack s = new Stack("Stack");
		Producer p = new Producer("Producer",s);
		Consumer c1 = new Consumer("Consumer1",s);
		Consumer c2 = new Consumer("Consumer2",s);
	}
}
class Consumer extends Thread{
	private Stack stack;
	public Consumer(String name,Stack s){
		super(name);
		stack = s;
		start();
	}
	public void run(){
		for(int i=0;i<100;i++){
			synchronized(stack){
			String good = stack.pop();
			System.out.println(getName() +" pop "+good);
			}
				try{
					Thread.sleep(1000);
				}
				catch(Exception e){}
			
		}
	}
}
class Producer extends Thread{
	private Stack stack;
	public Producer(String name,Stack s){
		super(name);
		stack = s;
		start();
	}
	public void run(){
		for(int i=0;i<200;i++){
				synchronized(stack){
				String good = "good "+(stack.getPoint()+1);
				stack.push(good);
				System.out.println(getName() +" push "+good);
				}
		}	
	}
}
class Stack{
	public final static int LEN = 100;
	private int point = -1;
	private String name;
	private String goods[] = new String[LEN];
	public Stack(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
	public synchronized int getPoint(){
		return point;
	}
	public synchronized String pop(){
		if(point==-1) return null;		//如果消费者不能拿到
		String tmp = goods[point];
		goods[point] = null;
		point--;
		return tmp;
 	}
	public synchronized void push(String name){
		if(point==LEN-1) return; 
		point++;
		goods[point] = name;
	}
}

ThreadLocal类


ThreadLocal类主要用来作为线程范围内的共享数据;
JavaWeb中访问Servlet时,每个线程都会有一个容器用来存放数据;此处就使用了ThreadLocal;
比如:


而一般实现此种需求需要使用map实现,如图:



而ThreadLocal则是类似于此种方式实现;ThreadLocal类提供了:
(1)ThreadLocal<Integer> map = new ThreadLocal<Integer>();   //创建一个ThreadLocal对象,存放Integer数据;
(2)map.set(1);   //为当前线程存放数据1;
(3)int data = map.get();  //从当前线程中取出数据1;
(4)map.remove(); //从当前线程中删除数据1;

package org.xiazdong.sync;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Demo04 {

	private static ThreadLocal<Integer> map2 = new ThreadLocal<Integer>();
	public static void main(String[] args) {
		for (int i = 0; i < 3; i++) {
			new Thread() {
				public synchronized void run() {
					Random random = new Random();
					int data = random.nextInt(100);
					map2.set(data);
					new A().fun();
				}
			}.start();
		}

	}
	static class A{
		public void fun(){
			System.out.println(Thread.currentThread().getName()+":"+map2.get());
		}
	}

}

创建线程范围内共享的对象:

注意:此类为singleton类,并且内置ThreadLocal;

特别注意:ThreadLocal的使用也只能在业务中使用,而不能在线程中使用,因此必须记住!

class Data {
	private String name;
	private int age;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	private static ThreadLocal<Data>map = new ThreadLocal<Data>();
	private Data(){}
	public static Data  getInstance(){
		Data instance = map.get();
		if(instance==null){
			instance = new Data();
			map.set(instance);
		}
		return instance;
	}
}

释放锁:

(1)正常执行完。

(2)因为异常而导致线程终止,则释放。


锁的使用方式:

(1)ReentrantLock

以上的unlock放在finally中因为防止抛出异常而没有释放锁;

特性:锁是可重入的,即如果一个线程得到了某个对象的锁,则可以随便怎么使用锁,即 可以多次使用锁。

(2)ReentrantReadWriteLock

    ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    Lock readLock=lock.readLock(); 从读写锁中抽取读锁

    Lock writeLock=lock.writeLock(); 从读写锁中抽取写锁



问题:设计一个缓存系统Cache


缓存定义:一开始在缓存中寻找,如果找不到,则去数据库中取,并放入缓存;


package org.xiazdong.sync;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class CacheData {
	private Map<String, Object> cache = new HashMap<String, Object>();
	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	//读写锁
	public Object get(String key) {
		//一进去上读锁
		lock.readLock().lock();
		Object value;
		try {
			value = cache.get(key);
			//如果找不到,则在数据库中读取,并写入cache,因此上写锁
			if (value == null) {
				lock.readLock().unlock();
				lock.writeLock().lock();
				try {
					if(value==null){
						value = DB.query(key);
                                                cache.put(key,value); 
                                        }
                                } finally {
					lock.writeLock().unlock();
				}
				lock.readLock().lock();
			}
		} finally {
			lock.readLock().unlock();
		}
		return value;

	}
}


条件变量的使用


条件变量是对于锁功能的补充,比如我们营造一个如下结构的程序: 

 

 

当多个线程同时访问这个transfer函数,总会有一个串行化顺序,比如Person1得到了这个函数的使用权,但是他现有资金不足以转出,则会停止,等待别人转入等到有足够的钱转出时才继续执行,但是他却没有放弃这个函数的使用权,因此其他人没有办法使用这个函数,因此也没有办法转给Person1钱,因此程序就无法继续。唯一的解决办法就是使用条件变量。

一个锁能有多个条件变量。

试图获得锁:tryLock()

lock.tryLock()如果能获得锁则返回true并获得锁,如果不能则不会阻塞,能够去做其他事。

lock.tryLock(long s,TimeUnit.MILLISECONDS)  试图获得锁,如果不能获得,则阻塞s秒后去做其他事。在阻塞时中断则抛出异常。

 

Condition:条件变量


Condition c=lock.newCondition();

java中默认每个对象都有一把隐式锁,一把隐式锁只有一个条件变量。因此老式的java程序中使用synchronized关键字进行同步。

 老式同步方法:synchronized 新式同步方法:锁+条件变量   Lock lock=new ReentrantLock();Condition c=lock.newCondition(); 初始化
 wait() c.await()放入条件变量的等待队列,并且释放c对应的锁,使得其他人能用这把锁。【阻塞状态】 阻塞时发生中断则抛出异常。
 wait(long milli) c.await(long s,TimeUnit.MILLISECONDS)相比较上面的方法,如果milli毫秒后会自动变回可运行状态。
 notifyAll() c.signalAll() 把条件变量的等待队列的线程全部放回。【可运行状态】
 notify() c.notify() 随机抽取条件变量等待队列中的一个线程放回变成可运行状态。

比较而言:synchronized比较方便。

缺点:以上的同步方法需要我们自己写代码进行同步,不是自动同步。

监视器:一种OO的同步方法,不需要考虑如何加锁。


用Condition替换Synchronized

形如:

lock.lock();
try{
	while(....)
		condition.await();

	condition.signal();
}
finally{
	lock.unlock();
}

用Condition实现先执行sub,再执行main、再执行sub;
package org.xiazdong.sync;

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

public class ConditionTest {

	public static void main(String[] args) throws Exception {
		final Business business = new Business();
		new Thread() {
			public void run() {
				while (true)
					try {
						business.sub();
					} catch (Exception e) {
						e.printStackTrace();
					}
			}

		}.start();
		Thread.sleep(10);
		new Thread() {
			public void run() {
				while (true)
					try {
						business.main();
					} catch (Exception e) {
						e.printStackTrace();
					}
			}
		}.start();
	}

	static class Business {
		boolean subfinished = false; // 记录是否该sub()执行
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();

		public void sub() throws Exception {
			lock.lock();
			while (subfinished) {

				condition.await();
			}
			for (int i = 0; i < 10; i++) {
				System.out.println("sub:" + (i + 1));
			}
			subfinished = true;
			condition.signal();
			lock.unlock();

		}

		public void main() throws Exception {
			lock.lock();
			while (!subfinished) {
				condition.await();
			}
			for (int i = 0; i < 100; i++) {
				System.out.println("main:" + (i + 1));
			}
			subfinished = false;
			condition.signal();
			lock.unlock();
		}
	}
}


condition实现多路同步


声明多个Condition变量;

任务:先执行1,再执行2,再执行3,再执行1......

package org.xiazdong.sync;

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

public class MultiConditionTest {

	public static void main(String[] args) {
		final Business1 bus = new Business1();
		for (int i = 0; i < 10; i++) {
			new Thread() {
				public void run() {
					bus.fun1();
				}
			}.start();
			new Thread() {
				public void run() {
					bus.fun2();
				}
			}.start();
			new Thread() {
				public void run() {
					bus.fun3();
				}
			}.start();
		}
	}

}

class Business1 {
	Lock lock = new ReentrantLock();
	Condition condition1 = lock.newCondition();
	Condition condition2 = lock.newCondition();
	Condition condition3 = lock.newCondition();
	int count = 0;

	public void fun1() {
		lock.lock();
		try {
			while (count != 0)
				try {
					condition1.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println("1");
			count = (count + 1) % 3;
			condition2.signal();
		} finally {
			lock.unlock();
		}

	}

	public void fun2() {
		lock.lock();
		try {
			while (count != 1)
				try {
					condition2.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println("2");
			count = (count + 1) % 3;
			condition3.signal();
		} finally {
			lock.unlock();
		}
	}

	public void fun3() {
		lock.lock();
		try {
			while (count != 2)
				try {
					condition3.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println("3");
			count = (count + 1) % 3;
			condition1.signal();
		} finally {
			lock.unlock();
		}
	}
}


在API文档中实现的阻塞队列:

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }




volatile变量:


对一个变量使用volatile,则会使得虚拟机和编译器知道这个变量会被并发访问。


死锁:线程T1,T2,T1有锁L1,T2有锁L2,如果T1想要L2的同时T2想要L1,则会发生死锁。

public class DeadLockDemo extends Thread{
	private Counter counter;
	public DeadLockDemo(Counter counter){
		this.counter = counter;
		start();
	}
	public void run(){
		for(int i=0;i<1000;i++){
			counter.add();
		}
	}
	public static void main(String args[]){
		Counter c1 = new Counter();
		Counter c2 = new Counter();
		c1.setFriend(c2);
		c2.setFriend(c1);
		DeadLockDemo m1 = new DeadLockDemo(c1);
		DeadLockDemo m2 = new DeadLockDemo(c2);
	}
}
class Counter{
	private int a;
	private Counter friend;
	public void setFriend(Counter friend){
		this.friend = friend;
	}
	public synchronized void add(){
		a++;
		Thread.yield();
		friend.delete();
		System.out.println(Thread.currentThread().getName()+": add");
	}
	public synchronized void delete(){
		a--;
		System.out.println(Thread.currentThread().getName()+": delete");
	}
}





废弃方法


suspend和resume:


suspend:暂停一个线程,不会放弃持有的锁.
resume:运行一个线程。
废弃原因:
(1)容易导致死锁
(2)一个线程能随时暂停另一个线程,不合理
public class SuspendDemo extends Thread{
	private int a;
	public void run(){
		for(int i=0;i<1000;i++){
			synchronized(this){
				a+=i;
				Thread.yield();
				a-=i;
			}
		}
	}
	public synchronized void reset(){
		a = 0;
	}
	public static void main(String args[])throws Exception{
		SuspendDemo s = new SuspendDemo();	//s获得SuspendDemo的锁
		s.start();
		yield();
		s.suspend();
		s.reset();	//main调用reset方法之前先要获得SuspendDemo的锁,但是此时s获得,并且暂停。
		s.resume();
	}
}


stop:

stop终止这个线程.
废弃原因:
如果终止时该线程正在执行一个原子操作,则会产生逻辑不一致.



线程安全数据结构


阻塞队列:BlockingQueue        适合用于生产者-消费者模型


Queue是BlockingQueue的父类:


BlockingQueue提供的方法如下:


因此多个阻塞的两个方法。

(1)LinkedBlockingQueue<T>:基于链表,容量无限阻塞队列。

(2)ArrayBlockingQueue<T>:一个由数组支持的有界阻塞队列。有界缓冲区

-ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(n); //创建一个容量为n的阻塞队列;

(3)PriorityBlockingQueue<T>:无界阻塞优先级队列。

(4)DelayQueue<T>:队列中的元素都需要实现Delayed接口和Comparable接口。当元素的延迟用完(小于0)才能从队列中删除。

 


ArrayBlockingQueue代码示例:


package org.xiazdong.tradition;

import java.util.concurrent.ArrayBlockingQueue;

public class BlockingQueueTest {

	public static void main(String[] args) {
		final ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(
				5);
		new Thread() {
			public void run() {
				for (int i = 0; i < 10; i++) {
					synchronized (this) {
						
						try {
							Thread.sleep(100);
							queue.put(3);
						} catch (Exception e) {
							e.printStackTrace();
						}
						System.out.println("放入数据,目前有"+queue.size());
					}
				}
			}

		}.start();
		new Thread() {
			public void run() {
				for (int i = 0; i < 10; i++) {
					synchronized (this) {
						try {
							Thread.sleep(1000);
							queue.take();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("取出数据,目前有"+queue.size());
					}
				}
			}

		}.start();
	}

}


示例2:用ArrayBlockingQueue实现实现先线程1走,再线程2走,再线程1走,再线程2走....




package org.xiazdong.tradition;

import java.util.concurrent.ArrayBlockingQueue;

public class CommunicationBlockingQueueTest {

	public static void main(String[] args) {
		final Business business = new Business();
		new Thread() {
			public void run() {
				for(int i=0;i<50;i++){
					business.sub();
				}
			}

		}.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for(int i=0;i<50;i++)
			business.main();

	}
	static class Business{
		private ArrayBlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1);
		private ArrayBlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1);
		public Business(){
			try {
				queue2.put(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		public void sub(){
			
			for (int i = 0; i < 10; i++) {
				try {
					queue2.take();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("sub:" + i);
				try {
					queue1.put(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
		public void main(){
			
			for (int i = 0; i < 100; i++) {
				try {
					queue1.take();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("main:" + i);
				try {
					queue2.put(2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
		}
	}

}

ArrayBlockingQueue在现实中非常实用.


线程安全集合


一般的ArrayList、HashMap、HashSet等集合都不是线程安全的;

并发库中提供了:
(1)CopyOnWriteArrayList: 多线程的ArrayList;
(2)ConcurrentSkipListHashMap:多线程的HashMap;

小问题:HashMap和HashSet在实现上有什么关联?


答:HashSet在内部实现是通过HashMap的key实现的;

并发队列ConcurrentLinkedQueue:一个基于链接节点的无界线程安全队列。

1.offer

2.poll

3.peek

并发散列映射表ConcurrentHashMap<K,V>:支持多个读写器

下面是原子性操作:

1.putIfAbsent(key,value):添加                

  •    if (!map.containsKey(key)) 
          return map.put(key, value);
      else
           return map.get(key);
    • 如果key原来没有,则插入。
    • 如果key原来有,则返回旧值。
3.remove(key,value):移除key-value对,如果不在映射中,则不执行任何操作    
  • if (map.containsKey(key) && map.get(key).equals(value)) {
           map.remove(key);
           return true;
       } else return false;    
4.replace(key,oldvalue,newValue):替换  
  • if (map.containsKey(key) && map.get(key).equals(oldValue)) {
           map.put(key, newValue);
           return true;
       } else return false;   

写时复制数组CopyOnWriteArryaList<T>:每个修改他的线程都有这个数组的一份拷贝。

任何collection类通过包装器能够变为线程安全。


Callable和Future


Callable<T>:类似Runnable,唯一的区别就是前者有返回值,后者无返回值,因此前者可以用于异步计算。

Future<T>用于保存Callable异步计算的结果。

1.get() 计算完之前阻塞,计算好了则返回结果。

2.isDone()返回是否计算完毕

3. future.get(int seconds ,TimeUnit.SECONDS)  在seconds秒内一定要得到结果,不然抛出异常;

实例代码:

package org.xiazdong.thread;

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

public class Demo03 {

	public static void main(String[] args) throws Exception {
		ExecutorService service = Executors.newSingleThreadExecutor();
		Future<String> future = service.submit(new Callable<String>(){
			@Override
			public String call() throws Exception {
				return "Hello";
			}
		});
		System.out.println(future.get());
		
	}

}

同时提交多个Callable并返回多个Future

2.提交作业:

(1)提交一个任务

  •     Future<T> result=pool.submit(Callable<T>task)
  •     pool.execute(Runnable task);

(2)提交多个任务

  • ArrayList<Callable<T>>task;
  • List<Future<T>>results=pool.invokeAll(task);  提交所有任务。

Future<T>result=pool.invokeAny(task); 任意提交其中一个已完成任务。

3.result.get()可得结果

4.pool.sutdown()在用完线程池后关闭。


package org.xiazdong.sync;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableFuture {

	public static void main(String[] args) throws Exception, ExecutionException {
		ExecutorService service = Executors.newFixedThreadPool(3);
		ArrayList<Callable<Integer>> list = new ArrayList<Callable<Integer>>();
		for (int i = 0; i < 10; i++) {
			final int a = i;
			list.add(new Callable<Integer>() {

				@Override
				public Integer call() throws Exception {
					System.out.println(Thread.currentThread().getName());
					return a;
				}

			});
		}
		List<Future<Integer>> results = service.invokeAll(list);
		for (Future<Integer> f : results) {
			if (f.isDone())
				System.out.println(f.get());
		}
	}

}


多线程实现另外几种方法:


1.FutureTask类


结合以上两个接口,FutureTask是实现了以上两个接口的类,能够把Callable作为参数传进去,可以利用FutureTask得到Callable计算的结果。

FutureTask<T> implements Runnable,Future<T>

构造:FutureTask(Callable<T>c);

放入线程:Thread t=new Thread(ft);

开始执行:t.start();

取得结果:ft.get();

2.Executors类       线程池类

线程池的优点:

(1)线程可以重用。

(2)限制线程个数。

线程池的变种:

1.创建线程池

(1)ExecutorService pool=Executors.newCachedThreadPool():在必要的时候能创建线程

(2)ExecutorService pool=Executors.newFixedThreadPool(int n);在线程池中创建固定数量的线程

(3) ExecutorService pool=Executors.newSingleThreadExecutor();线程池中只有一个线程,顺序执行线程。

pool.execute(Runnable run);  //执行runnable的run方法;

pool.shutdown();   在没有任务时结束线程池;

预定时间执行线程池:

ScheduledExecutorService pool=Executors.newScheduledThreadPool(int threads);线程池中只有一个线程,顺序执行线程。

pool.schedule(Runnable run,int delay,TimeUnit unit); 在delay的单位时间后启动任务。

pool.scheduleAtFixedRate(Runnable com,long initialDelay,long period,TimeUnit); initialDelay秒后周期性的启动任务。

package org.xiazdong.sync;

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

public class Demo07 {

	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(3); //创建一个只能容纳3个线程的线程池
		for (int i = 0; i < 10; i++) {
			pool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()); //Thread-name只能出现1,2,3
				}

			});
		}
		pool.shutdown();   //没有任务后线程池销毁
	}

}

package org.xiazdong.sync;

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

public class Demo07 {

	public static void main(String[] args) {
		ExecutorService pool = Executors.newSingleThreadExecutor(); //创建一个只能容纳1个线程的线程池
		for (int i = 0; i < 10; i++) {
			pool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()); //Thread-name只能出现1
				}

			});
		}
		pool.shutdown();   //没有任务后线程池销毁
	}

}


package org.xiazdong.sync;

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

public class Demo07 {

	public static void main(String[] args) {
		ExecutorService pool = Executors.newCachedThreadPool(); //创建一个只能容纳1个线程的线程池
		for (int i = 0; i < 100; i++) {
			pool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()); //Thread-name只能出现1
				}

			});
		}
		pool.shutdown();   //没有任务后线程池销毁
	}

}


问:如何实现一个线程死了之后再启动一个线程?


答:用单线程池实现;




CompletionService类 :提交多个任务


(1)CompletionService<Integer> service = new ExecutorCompletionService<Integer>(Executor pool);    //创建一个执行多个任务的服务,此任务的返回结果是Integer并且接收线程池,;

(2)service.submit(new Callable<Integer>(){});   //提交任务;

(3)Future<Integer> future = service.take();//获得先完成的任务的结果;

(4)Integer data = future.get();  //获得结果

package org.xiazdong.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo03 {

	public static void main(String[] args) throws Exception {
		ExecutorService pool = Executors.newSingleThreadExecutor();
		CompletionService<Integer> service = new ExecutorCompletionService<Integer>(pool);
		for(int i=0;i<100;i++){
			final int s = i;
			service.submit(new Callable<Integer>(){
	
				@Override
				public Integer call() throws Exception {
					Thread.sleep(100);
					return s;
				}
				
			});
		}
		for(int i=0;i<100;i++){
			Future<Integer> result = service.take();
			System.out.println(result.get());
		}
	}

}



控制线程组:ExecutorCompletionService<T>  内含阻塞队列

    构造:ExecutorCompletionService<T> ecs=new ExecutorCompletionService(Executor e);
    提交:ecs.submit(Callable<T>c);
    取得结果是一个阻塞队列,队列中的元素是Future<T>:ecs.take().get();
一些同步方法:
1.CyclicBarrier 栅栏:顾名思义,就是如果在代码某处设个栅栏,则线程会执行到那停止,等到所有线程都到了,再一起执行。
    CyclicBarrier cb=new CyclicBarrier(int n,Runnable action); 规定n个线程,如果n个线程到齐,则执行action。
    cb.await(); 
    cb.await(int n,TimeUnit);
2.倒计时门栓CountDownLatch 等到count变为0才开始执行。
    CountDownLatch cdl=new CountDownLatch(int count);
    cdl.await();等待直到count=0;
    countDown();
3.同步队列SynchronousQueue<T>:put后阻塞等待take,take时阻塞等待put
  • 例:put(1);后则会阻塞,直到调用take()获取1为止。
  • 例:take()后会阻塞直到放入一个元素为止。

4.semaphore:

  • 构造:Semaphore(int n);  初始为n的信号量
  • acquire()     n<=0时阻塞,否则n--;
  • release()     n++,释放acquire阻塞的线程。

Swing与线程:

Swing中最基本的线程:

  • main线程
  • 实现分派线程   接收actionPerformed或paintComponent
  • gc垃圾回收线程

Swing不是线程安全的。

设置组件属性一定要在组件实现之前。

setVisible(),pack,add称为组件实现。

在Java中,键盘输入、鼠标点击或者应用程序本身产生的请求会被封装成一个事件,放进一个事件队列中,java.awt.EventQueue对象负责从这个队列中取出事件并派发它们。而EventQueue的派发有一个单独的线程管理,这个线程叫做事件派发线程(Event Dispatch Thread),也就是EDT。此外,Swing的绘制请求也是通过EDT来派发的。

EventQueue.invokeLater(Runnable r);把r任务放到事件队列中等待事件分派线程执行。

当需要更新Swing的内容时,则需要将这段代码放入r类中。

 

native关键字由java调用本机的系统函数。



java.util.concurrent.atomic 包


此包提供了原子操作的数据类型,比如说AtomicInteger表示原子操作的int;AtomicIntegerArray表示原子操作的int[];AtomicIntegerFieldUpdater表示原子操作类中的某个属性;

1.AtomicInteger

提供了常用的方法:

(1)AtomicInteger a = AtomicInteger(int initialValue)   //初始化a为整数initialValue;

(2)int data = a.addAndGet(int delta) ;   //  data = a+delta;

(3)int data = a.decrementAndGet() ;    // data = a-1;

(4)int data = a.incrementAndGet();  // data = a+1;

(5)a.set(int value); //将a设置为value;


2.AtomicIntegerFieldUpdater

此类可以表示某个类中的整数属性,此属性必须是volatile的

提供了常用的方法:

(1)AtomicIntegerFieldUpdater<U> field = AtomicIntegerFieldUpdater.newUpdater(Class<U> tclass,String fieldName)   //创建tclass类的fieldName属性;

(2)int  data =  field.addAndGet(T obj, int delta)   //data = obj对象的field属性值+delta;

(3)int data = field. decrementAndGet(T obj) ;    //  --obj对象;

(4)int data = field.get(T obj);   //获得obj对象的属性值;


3.AtomicIntegerArray

此类代表整形数组;

常用方法;

(1)AtomicIntegerArray array = new AtomicIntegerArray(int length);   //创建长度为length的数组;

(2)AtomicIntegerArray array = new AtomicIntegerArray(int[]arr);  //array = int[]arr;

(3)array.set(int i,int value);  // arr[i] = value;

(4)int a = array.get(i);

package org.xiazdong.sync;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class Demo06 {

	private static AtomicInteger a = new AtomicInteger(3);
	private static AtomicIntegerFieldUpdater<Person> field = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");
	private static AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1,2,3});
	public static void main(String[] args) {
		final Person p = new Person();
		new Thread(){
			public void run(){
				int b = a.addAndGet(3);
				int elem = array.get(1);
				array.addAndGet(1, 5);
				field.set(p,20);
				System.out.println("array-1: from "+elem+" to "+array.get(1));
				System.out.println("field:"+field.get(p));
				System.out.println("int:"+b);
			}
		}.start();
		new Thread(){
			public void run(){
				int b = a.decrementAndGet();
				System.out.println(b);
			}
		}.start();
	}

}
class Person{
    volatile int age;
    
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
} 

Semaphore:信号量


实现功能:有一个厕所,只有5个位置,因此第5个人进去以后,就不能有第6个人进去;即控制只能有规定数量的线程进入;
(1)Semaphore sp = new Semaphore(n); //创建只能有n个线程进入的
(2)int available = sp.availablePermits(); //返回还有几个线程可以进入;
(3)sp.require();//获得信号量
(4)sp.release(); //释放信号量

注意:谁都能释放信号量;如果信号量为1,则就是同步锁;

应用场景:停车位,厕所,占位问题;

package org.xiazdong.sync;

import java.util.concurrent.Semaphore;

public class SemaphoreTest {

	public static void main(String[] args) {
		final Business bus = new Business();
		for(int i=0;i<10;i++){
			new Thread(){
				public void run(){
					bus.fun();
				}
			}.start();
		}
	}
	static class Business{
		Semaphore sp = new Semaphore(3);
		public void fun(){
			try {
				sp.acquire();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"进入"+",还剩"+sp.availablePermits());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sp.release();
			System.out.println(Thread.currentThread().getName()+"离开"+",还剩"+sp.availablePermits());
		}
	}
}


CyclicBarrier


常用方法:

(1)CyclicBarrier barrier = new CyclicBarrier(3); //创建一个栅栏,只要3个线程等着,就再执行;

(2)barrier.await(); //在栅栏处等着;

(3)barrier.getNumberWaiting(); //返回有几个线程在栅栏处等着;


一般来说,多个线程都会并发执行,谁也不会等谁,但是如果有了栅栏,则多个线程执行到一个栅栏时,则会都等到那,只有规定线程都执行到那,然后再一起继续执行,如图:


实现代码:

package org.xiazdong.sync;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {

	public static void main(String[] args) throws Exception {
		final CyclicBarrier barrier = new CyclicBarrier(3);
		System.out.println("一共有三个同学:");
		for(int i=0;i<3;i++){
			
			Thread.sleep(1000);
			new Thread(){
				public void run(){
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e3) {
						e3.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"到达");
					System.out.println((barrier.getNumberWaiting()+1)+"个线程正在等");
					if((barrier.getNumberWaiting()+1)==3){
						System.out.println("出发!...");
					}
					try {
						barrier.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (BrokenBarrierException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					
					
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e2) {
						// TODO Auto-generated catch block
						e2.printStackTrace();
					}
					
				}
			}.start();
		}
	}

}


CountDownLatch:倒计时器


常用方法:

(1)CountDownLatch latch = new CountDownLatch(n); //创建数字为n的倒计时器
(2)latch.await(); //等待,直到倒计时器为0;
(3)latch.countDown(); //倒计时器减一;
实现运动员比赛:全部运动员准备好后,直到裁判发令起跑;
package org.xiazdong.sync;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

	public static void main(String[] args) throws Exception {
		
		final CountDownLatch latch1 = new CountDownLatch(1);
		final CountDownLatch latch2 = new CountDownLatch(8);
		ExecutorService pool = Executors.newCachedThreadPool();
		for(int i=0;i<8;i++){
			final int tmp = i;
			pool.execute(new Runnable(){

				@Override
				public void run() {

					System.out.println("运动员"+tmp+"准备好了,等待...");
					try {
						latch1.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					try {
						Thread.sleep(new Random().nextInt(1000));
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("运动员"+tmp+"到达...");
					latch2.countDown();
				}
				
			});
		}
		Thread.sleep(1000);
		System.out.println("裁判法令,开始起跑!");
		latch1.countDown();
		latch2.await();
		System.out.println("全体运动员到达..");
		pool.shutdown();
		
	}

}










Exchanger


两个线程交换数据;

场景:两人约定某地点,交换东西;

(1)Exchanger<T> exchanger = new Exchanger<T>(); //定义一个交换器,并且交换T类型的东西;

(2)T b = exchanger.exchange(T a) ; //用a交换,换回了b;


package org.xiazdong.sync;

import java.util.concurrent.Exchanger;

public class ExchangerTest {

	public static void main(String[] args) throws Exception {
		final Exchanger<Integer> ex = new Exchanger<Integer>();
		for(int i=0;i<2;i++){
			Thread.sleep(1000);
			final int tmp = i;
			new Thread(){
				public void run(){
					try {
						System.out.println(Thread.currentThread()+"到达持有:"+tmp);
						int a = ex.exchange(tmp);
						System.out.println(Thread.currentThread()+"换回:"+a);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
			}.start();
		}
	}

}


小知识点:静态内部类


如果要让内部类的行为类似于外部类的行为,则可以将内部类声明为static的;

原本一般的内部类对象创建需要依赖于外部类对象的创建,但是静态内部类则不需要;

小知识点:如果复制了源代码,则在Eclipse下的正确的包中 ctrl+v ,则可自动生成一个Java文件;















               
               


               
               










               
               


               
               












               
               












               
               












               
               












               
               












               
               

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值