基础加强_线程技术相关类一

  ------- android培训、java培训、期待与您交流! ----------

定时器Timer类的应用

import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;


public class TraditionalTimerTest {


	private static int count = 0;
	public static void main(String[] args) {
		
		class MyTimerTask extends TimerTask{			
			@Override
			public void run() {
				count = (count+1)%2;
				System.out.println("bombing!");
				//方法中新建一个计时器,执行本类任务
				new Timer().schedule(new MyTimerTask(),2000+2000*count);
			}
		}
		//启动计时器
		new Timer().schedule(new MyTimerTask(), 2000);
		
		while(true){
			//每秒打印一次秒数
			System.out.println(Calendar.getInstance().get(Calendar.SECOND));//new Date().getSeconds()
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

ThreadLocal实现线程范围的共享变量

每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,释放内存。线程结束后也可以自动释放相关的ThreadLocal变量
ThreadLocal的应用场景:
订单处理包含一系列操作:
减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。
银行转账包含一系列操作: 
把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。
例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管在哪个模块中调用getContext方法,拿到的都是同一个。

实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。
 
import java.util.Random;
public class ThreadLocalTest {

	private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
	public static void main(String[] args) {
		for(int i=0;i<2;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {
					int data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					//线程中向ThreadLocal中放的数据是和当前线程相关的
					x.set(data);
					MyThreadScopeData.getThreadInstance().setName("name" + data);
					MyThreadScopeData.getThreadInstance().setAge(data);
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
	
	static class A{
		public void get(){
			int data = x.get();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);


			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());
		}
	}
	
	static class B{
		public void get(){
			int data = x.get();			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());			
		}		
	}
}
将MyThreadLocalData封装成具有业务功能的对象,
每个线程只能有一个该类的实例对象,和单例模式差不多。
class MyThreadScopeData{
	private MyThreadScopeData(){}
	//静态的ThreadLocal实例,每个线程访问的是同一个map对象
	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
	//不需要加同步,因为对象是和线程相关的。
	public static MyThreadScopeData getThreadInstance(){
		MyThreadScopeData instance = map.get();
		if(instance == null){
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}
	
	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;
	}
}

小面试题

设计4个线程,其中2个线程每次对j增加1,另外2个线程每次对j减少1;
该方法没有实现顺序
public class ThreadTest1 { 
	//定义j变量,成员位置
	private int j; 


	public static void main(String args[]){ 
		//注意创建内部类对象的方法
		ThreadTest1 tt=new ThreadTest1(); 
		Inc inc=tt.new Inc(); 
		Dec dec=tt.new Dec(); 
		for(int i=0;i<2;i++){ 
			Thread t=new Thread(inc); 
			t.start(); 
			t=new Thread(dec); 
			t.start(); 
		} 
	} 
	//将j++和j--封装成方法,可以加同步
	private synchronized void inc(){ 
		j++; 
		System.out.println(Thread.currentThread().getName()+"-inc:"+j); 
	} 


	private synchronized void dec(){ 
		j--; 
		System.out.println(Thread.currentThread().getName()+"-dec:"+j); 
	} 
	//定义两个Runnable内部类,用于创建进程
	class Inc implements Runnable{ 
		public void run(){ 
			for(int i=0;i<100;i++){ 
				inc(); 
			} 
		} 
	} 


	class Dec implements Runnable{ 
		public void run(){ 
			for(int i=0;i<100;i++){ 
				dec(); 
			} 
		} 
	} 
} 

Java5的线程并发库

java.util.concurrent包
包含并发编程中很常用的实用工具类。
java.util.concurrent.atomic包
类的小工具包,支持在单个变量上解除锁的线程安全编程。
atomic包可以对基本数据,数组中的基本数据,对类中的基本数据进行操作。

线程并发库的应用
线程池的概念和Executors工具类的应用
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程,这就是封装。记住,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

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


public class ThreadPoolTest {


	/**
	 * 学习线程池的操作
	 * @param args
	 */
	public static void main(String[] args) {
		//ExecutorService threadPool = Executors.newFixedThreadPool(3);//创建固定大小为3个线程的线程池
		//ExecutorService threadPool = Executors.newCachedThreadPool();//创建一个可根据需要创建新线程的线程池
		ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单一线程的线程池
		for(int i=1;i<=10;i++){
			final int task = i;//局部内部类访问i可以采取这种方法
			threadPool.execute(new Runnable(){//execute接收一个Runnable对象,执行指定的任务
				@Override
				public void run() {
					for(int j=1;j<=10;j++){
						System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for  task of " + task);
					}
				}
			});
		}
		System.out.println("all of 10 tasks have committed! ");
		//threadPool.shutdownNow();//关闭执行任务,返回未执行的任务列表,threadPool.shutdown();按顺序关闭任务
		//用线程池启动定时器
		Executors.newScheduledThreadPool(3).scheduleAtFixedRate(
				new Runnable(){
					@Override
				public void run() {
					System.out.println("bombing!");					
				}},
				6,
				2,
				TimeUnit.SECONDS);
	}


}

Callable和Futrue的应用

Callable<T> 类似Runnable,call方法可以返回结果
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
public class CallableAndFuture {


	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ExecutorService threadPool =  Executors.newSingleThreadExecutor();//创建一个线程池
		Future<String> future =
			threadPool.submit(//通过线程池的submit方法提交Callable,返回future对象
				new Callable<String>() {
					public String call() throws Exception {
						Thread.sleep(2000);
						return "hello";
					};
				}
		);
		System.out.println("等待结果");
		try {
			System.out.println("拿到结果:" + future.get());//通过future的get方法获得返回值
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		ExecutorService threadPool2 =  Executors.newFixedThreadPool(10);
		CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2);
		for(int i=1;i<=10;i++){
			final int seq = i;
			completionService.submit(new Callable<Integer>() {
				@Override
				public Integer call() throws Exception {
					Thread.sleep(new Random().nextInt(5000));
					return seq;
				}
			});
		}
		for(int i=0;i<10;i++){
			try {
				System.out.println(
						completionService.take().get());//take方法返回已经完成的future
			} catch (Exception e) {
				e.printStackTrace();
			} 
		}
	}
}

  ------- android培训、java培训、期待与您交流! ----------

你可能感兴趣的:(多线程,java基础,黑马程序员)