------- android培训、java培训、期待与您交流! ----------
传统线程技术回顾
1.线程就是程序的一条执行的线路。多线程并不一定会提高程序的运行效率。
2.创建线程的两种传统方式
(1)创建Thread的子类,覆盖其中的run方法。
Thread thread1 =new Thread(){
public void run()
try{
Thread.seelp(20) ;让线程暂停
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread. currentThread().getName());//获取当前线程对象名字
}
} .start();
(2)在传递给Thread对象的Runnable对象的run方法中编写代码。
Thread thread2 =new Thread(new Runnable(){
public void run() {
while(true){
try {
Thread.sleep(500);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}).start();
总结:
由Thread类中的run方法源代码中看出,两种传统创建线程的方式都是在调用Thread对象的run方法,如果Thread对象的run方法没有被覆盖,并且像上边的问题那样为Thread对象传递了一个Runnable对象,就会调用Runnable对象的run方法。
传统定时器技术回顾
传统定时器的创建:直接使用定时器类Timer
示例
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TraditionalTimerTest {
privatestatic int count = 0;
public staticvoid main(String[] args) {
classMyTimerTask extends TimerTask{
@Override
public void run() {
count= (count+1)%2;
System.out.println("bombing!");
newTimer().schedule(new MyTimerTask(),2000+2000*count);
}
}
new Timer().schedule(new MyTimerTask(),2000);
while(true){
System.out.println(newDate().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
传统线程互斥技术
1.内部类不能访问局部变量,要访问需加final
2.静态方法中不能创建内部类的实例对象
3.互斥方法:
同步代码块:synchronized (lock){}。
同步方法:方法返回值前加synchronized,同步方法上边用的锁就是this,对象静态同步方法使用的锁是该方法所在的class文件对象。
4.使用synchronized关键字实现互斥,要保证同步的地方使用的是同一个锁对象
传统线程同步通信技术
1.锁是上在代表要操作的资源类的内部方法中的,而不是上在线程代码中的。这样写出来的类就是天然同步的,只要使用的是同一个new出来的对象,那么这个对象就具有同步互斥特性。
2.判断唤醒等待标记时使用while增加程序健壮性,防止伪唤醒。
ThreadLocal类及应用技巧
1.JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于上一节中的Map。
2.每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map集合中增加一条记录,key就是各自的线程,value就是各自的set方法传进去的值。
3.在线程结束时可以调用ThreadLocal.clear()方法用来更快释放内存,也可以不调用,因为线程结束后也可以自动释放相关的ThreadLocal变量。
4.一个ThreadLocal对象只能记录一个线程内部的一个共享变量,需要记录多个共享数据,可以创建多个ThreadLocal对象,或者将这些数据进行封装,将封装后的数据对象存入ThreadLocal对象中。
示例
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class ThreadLocalTest {
privatestatic ThreadLocal<Integer> x = new ThreadLocal<Integer>();
privatestatic ThreadLocal<MyThreadScopeData> myThreadScopeData =
newThreadLocal<MyThreadScopeData>();
publicstatic void main(String[] args) {
for(inti=0;i<2;i++){
newThread(new Runnable(){
@Override
publicvoid run() {
intdata = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+ " has put data :" + data);
x.set(data);
MyThreadScopeData.getThreadInstance().setName("name"+ data);
MyThreadScopeData.getThreadInstance().setAge(data);
newA().get();
newB().get();
}
}).start();
}
}
static class A{
publicvoid get(){
intdata = x.get();
System.out.println("Afrom " + Thread.currentThread().getName() + " get data :" +data);
MyThreadScopeDatamyData = MyThreadScopeData.getThreadInstance();
System.out.println("A from " +Thread.currentThread().getName() + " getMyData: " + myData.getName()+ "," +myData.getAge());
}
}
线程并发库的应用
1.线程池:先创建多个线程放在线程池中,当有任务需要执行时,从线程池中找一个空闲线程执行任务,任务完成后,并不销毁线程,而是返回线程池,等待新的任务安排。
2.线程池编程中,任务是提交给整个线程池的,并不是提交给某个具体的线程,而是由线程池从中挑选一个空闲线程来运行任务。一个线程同时只能执行一个任务,可以同时向一个线程池提交多个任务。
3.线程池创建方法:
(1)创建一个拥有固定线程数的线程池。
ExecutorService threadPool =Executors.newFixedThreadPool(2);
(2)创建一个缓存线程池。
ExecutorService threadPool = Executors.newCacheThreadPool();
(3)创建一个只有一个线程的线程池。
ExecutorService threadPool =Executors.newSingleThreadExector();
(4)往线程池中添加任务。
threadPool.executor(Runnable)
(5)关闭线程池
threadPool.shutdown() 线程全部空闲,没有任务就关闭线程池。
threadPool.shutdownNow() 不管任务有没有做完,都关掉。
Callable与Future的应用
1.获取一个线程的运行结果。
2.public interface Callable<V>
Future取得的结果类型和Callable返回的结果类型必须一致,通过泛型实现。Callable要通过ExecutorService的submit方法提交,返回的Future对象可以取消任务。
3.public interface Future<V>
Future 表示异步计算的结果。
4. CompletionService用于提交一组的Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
示例
import java.util.Random;
import java.util.concurrent.Callable;
importjava.util.concurrent.CompletionService;
importjava.util.concurrent.ExecutionException;
importjava.util.concurrent.ExecutorCompletionService;
importjava.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class CallableAndFuture {
publicstatic void main(String[] args) {
ExecutorServicethreadPool = Executors.newSingleThreadExecutor();
Future<String>future =
threadPool.submit(
newCallable<String>() {
publicString call() throws Exception {
Thread.sleep(2000);
return "hello";
};
}
);
System.out.println("等待结果");
try{
System.out.println("拿到结果:" + future.get());
}catch (InterruptedException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
ExecutorService threadPool2 =
Executors.newFixedThreadPool(10);CompletionService<Integer>
completionService = newExecutorCompletionService<Integer>(threadPool2);
for(inti=1;i<=10;i++){
finalint seq = i;
completionService.submit(newCallable<Integer>() {
@Override
publicInteger call() throws Exception {
Thread.sleep(newRandom().nextInt(5000));
returnseq;
}
});
}
for(inti=0;i<10;i++){
try{
System.out.println(
completionService.take().get());
}catch (InterruptedException e) {
e.printStackTrace();
}catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
线程锁技术
1.Lock比传统线程模型中的synchronized更加面向对象,锁本身也是一个对象,两个线程执行的代码要实现同步互斥效果,就要使用同一个锁对象。锁要上在要操作的资源类的内部方法中,而不是线程代码中。
2.锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。
条件阻塞Condition的应用
1.Condition 将 Object 监视器方法(wait、notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
示例
importjava.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
public class ThreeConditionCommunication {
publicstatic void main(String[] args) {
finalBusiness business = new Business();
newThread(
newRunnable() {
@Override
publicvoid run() {
for(inti=1;i<=50;i++){
business.sub2(i);
}
}
}
).start();
newThread(
newRunnable() {
@Override
publicvoid run() {
for(inti=1;i<=50;i++){
business.sub3(i);
}
}
}
).start();
for(inti=1;i<=50;i++){
business.main(i);
}
}
staticclass Business {
Locklock = new ReentrantLock();
Conditioncondition1 = lock.newCondition();
Conditioncondition2 = lock.newCondition();
Conditioncondition3 = lock.newCondition();
private int shouldSub = 1;
public void sub2(int i){
lock.lock();
try{
while(shouldSub != 2){
try {
condition2.await();
}catch (Exception e) {
e.printStackTrace();
}
}
for(intj=1;j<=10;j++){
System.out.println("sub2thread sequence of " + j + ",loop of " + i);
}
shouldSub = 3;
condition3.signal();
}finally{
lock.unlock();
}
}
public void sub3(int i){
lock.lock();
try{
while(shouldSub != 3){
try {
condition3.await();
}catch (Exception e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
for(intj=1;j<=20;j++){
System.out.println("sub3thread sequence of " + j + ",loop of " + i);
}
shouldSub = 1;
condition1.signal();
}finally{
lock.unlock();
}
}
public void main(int i){
lock.lock();
try{
while(shouldSub != 1){
try{
condition1.await();
}catch (Exception e) {
e.printStackTrace();
}
}
for(intj=1;j<=100;j++){
System.out.println("mainthread sequence of " + j + ",loop of " + i);
}
shouldSub= 2;
condition2.signal();
}finally{
lock.unlock();
}
}
}
}
Semaphore同步工具
1.Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。
2.Semaphore 通常用于限制可以访问某些资源的线程数目。
Semphore s = newSemphore(5);
s.acquire();//请求
s.release();//释放,添加一个许可,从而可能释放一个正在阻塞的获取者。
CyclicBarrier同步工具
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后,该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态。
CyclicBarrier cb= new CyclicBarrier(2);
cb.getNumberWaiting()当前等待个数
cb.await();等待
CountDownLatch同步工具
1.倒计时计数器,调用CountDownLatch对象的countDown方法。
2.CountDownLatch 的一个有用特性是,在countDown 方法的所有线程都能通过之前,阻止任何线程继续通过一个await。
Exchanger同步工具
实现两个人之间的数据交换。
exchanger.eachange(data)//将自己的数据交换出去
Thread.sleep(Random)换的过程
exchange ():等待另一个线程到达此交换点,然后将给定的线程传送给该线程,并接受该线程的对象。
阻塞队列的应用(ArrayBlockingQueue)
1.固定长度的队列往里放数据,如果放满了还要放,阻塞式队列就会等待,直到有数据取出,空出位置后才继续放;非阻塞式队列报错。
2.两个附加操作的Queue:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。
3.BlockingQueue 方法四种形式
(1)抛出一个异常。
(2)返回一个特殊值(null 或 false,具体取决于操作)。
(3)在操作可以成功前,无限期地阻塞当前线程。
(4)在放弃前只在给定的最大时间限制内阻塞。
示例
public staticvoid main(String[] args) {
final BlockingQueue<Integer> queue =new ArrayBlockingQueue<Integer>(3);
//该队列里面只能放3个Integer
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() +"准备放数据!");
queue.put(1);//如果放满就会阻塞
System.out.println(Thread.currentThread().getName()+ "已经放了数据,"+ "队列目前有" +queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
new Thread(){
public void run(){
while(true){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +"准备取数据!");
queue.take();//如果没有了数据,就会阻塞
System.out.println(Thread.currentThread().getName()+ "已经取走数据,"+
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
------- android培训、java培训、期待与您交流! ----------