本文主要是介绍黑马程序员---多线程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
TraditionalTread 传统线程技术回顾
创建线程的两种方式:
thread3中,同时创建了Thread的子类和Runnable,那么会优先执行Thread的子类,因为Runnable的代码被当做参数传到了Thread类里,Thread子类的run方法又覆盖了父类的方法,所以会执行Thread子类的代码。
package cn.itcast.heima;public class TraditionalThread {public static void main(String[] args) {Thread thread1 = new Thread() {public void run() {int i = 9;while(i>0){System.out.println(i--+"....."+Thread.currentThread().getName());System.out.println(i--+"....."+this.getName());}}};thread1.start();Thread thread2 = new Thread(new Runnable() {public void run() {int i = 9;while(i>0){System.out.println(i--+"......"+Thread.currentThread().getName());}}});thread2.start();new Thread(new Runnable() {public void run() {System.out.println("Runnable..."+Thread.currentThread().getName());}}) {public void run() {System.out.println("Thread..."+Thread.currentThread().getName());}}.start();}
}
运行结果:
9.....Thread-0
8.....Thread-0
7.....Thread-0
6.....Thread-0
5.....Thread-0
4.....Thread-0
3.....Thread-0
2.....Thread-0
1.....Thread-0
0.....Thread-0
9......Thread-1
8......Thread-1
7......Thread-1
6......Thread-1
5......Thread-1
4......Thread-1
3......Thread-1
2......Thread-1
1......Thread-1
Thread...Thread-2
多线程并不会提高程序的运行效率,反而会降低,因为就好比一个人在一张桌子上做馒头和这个人在十张桌子上做馒头,哪个会快呢?多线程下载是因为要去抢占服务器端的资源。
定时器的应用
void | schedule(TimerTask task,Date time) 安排在指定的时间执行指定的任务。 |
abstract void | run() 此计时器任务要执行的操作。 |
package cn.itcast.heima;import java.util.Date;
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{@Overridepublic void run() {count = (count+1)%2;System.out.println("bombing");new Timer().schedule(new MyTimerTask(), 2000+2000*count);}}new Timer().schedule(new MyTimerTask(),2000);//过2秒炸,过4秒炸,过2秒炸,过4秒再炸,循环。。。//秒表while(true) {System.out.println(new Date().getSeconds());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}//星期一到星期五每天凌晨三点收邮件,周六周日不收。这种问题可以用工具:quartz 来解决。
线程的同步互斥与通信 Synchronized
题目:使用两个线程打印两个字符串:“lihuoming”、“bixiangdong”,不加同步的话,会出现不完整名字。
package cn.itcast.heima;public class TraditionalThreadSynchronized {public static void main(String[] args) {//1.碰到多线程要操作同一份数据的时候,就要考虑线程安全的问题。/*2.静态方法不可以new内部类的实例对象。* 因为内部类是可以访问外部类的成员变量的。* 而成员变量一定是外部类产生实例对象的时候才分配内存空间的。* 而静态方法的使用可以不new对象,* 这时候内部类要访问外部类的成员变量,* 现在都没有产生外部类实例对象,* 去哪找成员变量的值呢?*/new TraditionalThreadSynchronized().init();}public void init() {final Outputer out = new Outputer();new Thread(new Runnable() {@Overridepublic void run() {while(true) {try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}out.output("lihuoming");}}}).start();new Thread(new Runnable() {@Overridepublic void run() {while(true) {try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}out.output2("bixiangdong");}}}).start();}class Outputer {public void output(String name) {int len = name.length();synchronized (this) {for (int i = 0; i < len; i++) {System.out.print(name.charAt(i));}System.out.println();}}public synchronized void output2(String name) {int len = name.length();synchronized (this) {for (int i = 0; i < len; i++) {System.out.print(name.charAt(i));}System.out.println();}} }
}
//方法上的synchronized,锁是this对象。
//静态方法上的synchronized,锁是类的字节码对象。
线程的同步互斥的图文解说
线程面试题:
子线程循环10次,主线程循环10次,子线程再循环10次,主线程再循环10次,如此循环50次。
package cn.itcast.heima;public class TraditionalThreadCommunication {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {@Overridepublic void run() {for(int j=1; j <= 50; j++) {business.sub(j);}}}).start();for(int j=1; j <= 50; j++) {business.main(j);}}
}class Business {private boolean bShouldSub = true;public synchronized void sub(int j) {//用while更健壮,防止伪唤醒。while(!bShouldSub) {try {this.wait();//这里synchronized的锁是什么对象,wait就用什么对象调用。} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("sub thread sequence of"+ i + ", loop of" + j);}bShouldSub = false;this.notify();//这里synchronized的锁是什么对象,notify就用什么对象调用。}public synchronized void main(int j) {while(bShouldSub) {try {this.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("main thread sequence of" + i+ ", loop of" + j);}bShouldSub = true;this.notify();}
}
//同步互斥的代码不是写在线程上,而是写在线程要访问的那个资源类的方法内部的。
//好处是以后任何线程要访问这个资源类,拿到以后就可以编译通过,很方便。
线程范围内共享数据 ThreadLocal
java.lang
类 ThreadLocal<T>
ThreadLocal 相当于一个 Map 集合。以线程名为key,线程内数据为value。ThreadLocal可以省去key,保证每个线程操作的都是属于这个线程自己的数据。
访问某个变量(通过其get 或set 方法)的每个线程都有自己的局部变量。
构造方法摘要 | |
---|---|
ThreadLocal() 创建一个线程本地变量。 |
方法摘要 | |
---|---|
T | get() 返回此线程局部变量的当前线程副本中的值。 |
protected T | initialValue() 返回此线程局部变量的当前线程的“初始值”。 |
void | remove() 移除此线程局部变量当前线程的值。 |
void | set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。 |
package cn.itcast.heima;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() {@Overridepublic void run() {int data = new Random().nextInt();//给全局静态变量data赋一个随机数。System.out.println(Thread.currentThread().getName()+ " has put data " + data);x.set(data);Student.getThreadInstance().setName("name"+data);Student.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);System.out.println("A from " + Thread.currentThread().getName()+ " get Student " + Student.getThreadInstance().getName()+ ", age" + Student.getThreadInstance().getAge());}}static class B {public void get() {int data = x.get();System.out.println("B from " + Thread.currentThread().getName()+ " get data " + data);System.out.println("B from " + Thread.currentThread().getName()+ " get Student " + Student.getThreadInstance().getName()+ ", age" + Student.getThreadInstance().getAge());}}static class Student {private Student() {}private static ThreadLocal<Student> threadLocalStu = new ThreadLocal<Student>();public static Student getThreadInstance() {Student stu = threadLocalStu.get();if(stu == null) {stu = new Student();threadLocalStu.set(stu);}return stu;}String name;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;}}}
//线程范围内的共享变量。这个学生去了不同的线程有不同的名字和年龄,入乡随俗。
运行结果:
Thread-1 has put data -661697870
Thread-0 has put data 863811802
A from Thread-0 get data 863811802
A from Thread-1 get data -661697870
A from Thread-0 get Student name863811802, age863811802
A from Thread-1 get Student name-661697870, age-661697870
B from Thread-0 get data 863811802
B from Thread-1 get data -661697870
B from Thread-1 get Student name-661697870, age-661697870
B from Thread-0 get Student name863811802, age863811802
多个线程之间共享数据
面试题:设计四个线程,其中两个线程每次对 j 增加一,另外两个线程每次对 j 减一。
package cn.itcast.heima;
/*一、如果线程执行代码相同,就可以把共享数据封装在一个Runnable里。例如:卖票。
public class MultiThreadShareData {public static void main(String[] args) {MyData data = new MyData(100);new Thread(data).start();new Thread(data).start();}
}
class MyData implements Runnable {private int count;MyData(int count){this.count = count;}@Overridepublic void run() {while(count>0){sale();}}private synchronized void sale(){if(count>0){count--;System.out.println(Thread.currentThread().getName()+"---sale---"+count);}}
}*//*二、如果线程执行代码不一样,例如:一个加加,一个减减。* 可以将数据封装在一个外部类里,并提供两个同步方法:一个加加方法和一个减减方法。* 创建两个Runnable类,一个调用加加方法,一个调用减减方法。
public class MultiThreadShareData {public static void main(String[] args) {MyData data = new MyData();new Thread(new Decrease(data)).start();new Thread(new Increase(data)).start();}
}
class Decrease implements Runnable{private MyData data;Decrease(MyData data){this.data = data;}@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<2;i++){data.decrease();}}
}
class Increase implements Runnable{private MyData data;Increase(MyData data){this.data = data;}@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<2;i++){data.increase();}}
}
class MyData {private int j;public synchronized void decrease(){j--;System.out.println(Thread.currentThread().getName()+"---dec---"+j);}public synchronized void increase(){j++;System.out.println(Thread.currentThread().getName()+"---inc---"+j);}
}*//*三、
public class MultiThreadShareData {//位置一:外部类的成员变量,要想被静态方法中的内部类所使用需加static关键字修饰。//private static MyData data = new MyData();public static void main(String[] args) {//位置二:局部变量,要想被方法中内部类使用,需加final关键字修饰。final MyData data = new MyData();new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<2;i++){data.decrease();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for(int i=0;i<2;i++){data.increase();}}}).start();}
}
class MyData {private int j;public synchronized void decrease(){j--;System.out.println(Thread.currentThread().getName()+"---dec---"+j);}public synchronized void increase(){j++;System.out.println(Thread.currentThread().getName()+"---inc---"+j);}
}*//*四、四个线程共享一个数据j,两个线程j++,两个线程j--*/
public class MultiThreadShareData {private int j;public static void main(String[] args){MultiThreadShareData tt = new MultiThreadShareData();Dec dec = tt.new Dec();Inc inc = tt.new Inc();for(int i=0; i<2; i++){Thread t = new Thread(dec);t.start();t = new Thread(inc);t.start();}}private synchronized void dec(){j--;System.out.println(Thread.currentThread().getName()+"---dec---"+j);}private synchronized void inc(){j++;System.out.println(Thread.currentThread().getName()+"---inc---"+j);}class Dec implements Runnable{@Overridepublic void run() {dec();}}class Inc implements Runnable{@Overridepublic void run() {inc();}}
}
运行结果:
Thread-0---dec----1
Thread-1---inc---0
Thread-2---dec----1
Thread-3---inc---0
Java5中的线程并发库
例如:
java.util.concurrent.atomic
类 AtomicInteger
加了同步以后的整数。只有在n个线程共同访问该整数变量的时候使用。不涉及到多线程时不需要使用。
构造方法摘要 | |
---|---|
AtomicInteger() 创建具有初始值 0 的新 AtomicInteger。 | |
AtomicInteger(int initialValue) 创建具有给定初始值的新 AtomicInteger。 |
方法摘要 | |
---|---|
int | addAndGet(int delta) 以原子方式将给定值与当前值相加。 |
int | decrementAndGet() 以原子方式将当前值减 1。 |
|
|
java.util.concurrent.atomic 包中的类:
AtomicInteger
AtomicIntegerArray (把整数放在数组中)
AtomicIntegerFieldUpdater (把整数放在成员变量的位置)
线程池 与 定时器
我们写一个tomcat服务器,难点就在于性能的快慢,线程的并发访问。为了每一个客户端连接进来服务器都可以接待,所以每一个客户端连接进来,我们都要启动一个线程。
package cn.itcast.heima;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ThreadPoolTest {public static void main(String[] args) {//ExecutorService threadPool = Executors.newFixedThreadPool(3);//固定线程的线程池。//ExecutorService threadPool = Executors.newCachedThreadPool();//有几个任务就创建几个线程的线程池。//单线程的线程池,它的特殊之处在于线程死了以后还会生成一个替补线程。永远保持有一个线程在那里。ExecutorService threadPool = Executors.newSingleThreadExecutor();for(int i=1;i<=6;i++){final int task = i; threadPool.execute(new Runnable(){@Overridepublic void run() {for(int j=1;j<=5;j++){System.out.println(Thread.currentThread().getName()+", is looping of "+j+", for task of "+task);}}});}System.out.println("All of 6 tasks hava committed.");threadPool.shutdown();//可被定时调度的线程池:ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);//线程池的延期调度:scheduledThreadPool.schedule(new Runnable(){@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+", bombing! task-1");}}, 5/*delay延期*/, TimeUnit.SECONDS);//线程池的固定频率调度:scheduledThreadPool.scheduleAtFixedRate(new Runnable(){@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+", bombing! task-2");}}, 6/*delay*/, 2/*period周期*/, TimeUnit.SECONDS);}}
java.util.concurrent
接口 ScheduledExecutorService 中:
所有的 schedule 方法都接受相对 延迟和周期作为参数,而不是绝对的时间或日期。将以 Date
所表示的绝对时间转换成要求的形式很容易。例如,要安排在某个以后的某个Date 运行,可以使用:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)。
Callable和Future
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
{ [自我见解] Callable和Runnable差不多。是可被调用的。不同之处在于:实现Runnable的类要覆盖run方法。实现Callable的类要覆盖call方法。
Future是线程池中的线程在执行某个任务后返回的结果。使用execute执行任务没有返回值。使用submit执行任务返回Future。
Future的泛型类型和Callable的泛型类型一致。}
package cn.itcast.heima;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 CallableAndFuture {public static void main(String[] args) {ExecutorService threadPool = Executors.newSingleThreadExecutor();Future<String> future = threadPool.submit(new Callable<String>(){@Overridepublic String call() throws Exception {Thread.sleep(2000);return "hello";}});try {System.out.println("future: "+future.get());} catch (InterruptedException | ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();} }
}
运行结果:
future: hello
CompletionService:
用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
好比我同时种了几块地的麦子,然后就等待收割。收割时,则是哪块先成熟了,则先去收割哪块麦子。
package cn.itcast.heima;import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class CallableAndFuture {public static void main(String[] args) {ExecutorService threadPool2 = Executors.newFixedThreadPool(10);//定义10个线程的线程池。//将线程池交给收割机调度。CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2);//提交10个任务for(int i=1; i<=10; i++) {final int sequence = i;//由收割机代替线程池提交任务。completionService.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {Thread.sleep(new Random().nextInt(5000));//每个任务小睡的时间不一样,最多不超过5秒。return sequence;//返回任务的序号。}});}for(int i=1; i<=10; i++) {try {Future<Integer> future = completionService.take();//谁先熟了,先收割谁。System.out.println(future.get());} catch (InterruptedException | ExecutionException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}
Lock&Condition实现线程同步通信
1.Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
java.util.concurrent.locks
接口Lock
所有已知实现类:ReentrantLock
一个可重入的互斥锁 Lock,它具有与使用 synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
package cn.itcast.heima;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockTest {public static void main(String[] args) {new LockTest().init();}public void init() {final Outputer out = new Outputer();new Thread(new Runnable() {@Overridepublic void run() {while(true) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}out.output("lihuoming");}}}).start();new Thread(new Runnable() {@Overridepublic void run() {while(true) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}out.output("bixiangdong");}}}).start();}class Outputer {Lock lock = new ReentrantLock();public void output(String name) {int len = name.length();lock.lock();//以防万一在执行里面程序的时候抛异常了,这个方法就退出了,不会执行到unlock方法,其他线程就进不去了。try{for (int i=0; i<len; i++) {System.out.print(name.charAt(i));}System.out.println();}finally{lock.unlock();}}}
}
运行结果:
lihuoming
bixiangdong
lihuoming
bixiangdong
bixiangdong
lihuoming
bixiangdong
lihuoming
bixiangdong
lihuoming
bixiangdong
lihuoming
bixiangdong
2.读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!
java.util.concurrent.locks 接口ReadWriteLock
所有已知实现类:ReentrantReadWriteLock
ReentrantReadWriteLock.ReadLock
readLock()
方法返回的锁。
ReentrantReadWriteLock.WriteLock
writeLock()
方法返回的锁。
package cn.itcast.heima;import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockTest {//static Queue queue = new Queue();public static void main(String[] args) {final Queue queue = new Queue();for(int i=0;i<3;i++){ //共开启3个读线程,3个写线程。new Thread(){public void run(){while(true){ //每开启一个线程,它就在不停的读。queue.get();}}}.start();new Thread(){public void run(){while(true){ //每开启一个线程,它就在不停的写。queue.set(new Random().nextInt(10000));}}}.start();}}
}
//因为读和写要操作的是同一个数据,那么读和写方法就得在同一个类里。
class Queue {private Object data = null;private ReadWriteLock rwl = new ReentrantReadWriteLock();public void get(){rwl.readLock().lock();try {System.out.println(Thread.currentThread().getName()+" be ready to read data!");Thread.sleep((long)(Math.random()*1000));System.out.println(Thread.currentThread().getName()+" hava read data "+data+" !");}catch (InterruptedException e) {e.printStackTrace();}finally {rwl.readLock().unlock();}}public void set(Object data){rwl.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+" be ready to write data!!");Thread.sleep((long)(Math.random()*1000));this.data = data;System.out.println(Thread.currentThread().getName()+" hava written data "+data+" !!");}catch (InterruptedException e) {e.printStackTrace();}finally {rwl.writeLock().unlock();}}
}
看API文档里的一段代码:
java.util.concurrent.locks
类 ReentrantReadWriteLock 里面的:
class CachedData {Object data;volatile boolean cacheValid;ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();void processCachedData() {rwl.readLock().lock();if (!cacheValid) {// Must release read lock before acquiring write lockrwl.readLock().unlock();rwl.writeLock().lock();// Recheck state because another thread might have acquired// write lock and changed state before we did.if (!cacheValid) {data = ...cacheValid = true;}// Downgrade by acquiring read lock before releasing write lockrwl.readLock().lock();rwl.writeLock().unlock(); // Unlock write, still hold read}use(data);rwl.readLock().unlock();}}
一个面试题:写一个缓存类:
package cn.itcast.heima;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/* 缓存类:* 你要找数据的时候先别找数据库,先找我,如果我没有你再找数据库。*/public class CacheDemo {//先定义一个Map,用来存放缓存数据。用户通过对象的名字进行查找。private Map<String,Object> cache = new HashMap<String,Object>();public static void main(String[] args) {}/*如果A、B、C三个线程同时来这里查,走到value==null的时候,要查几遍数据库啊,所以这里应该加上锁。* 多个读的时候,应该可以并发的,不会造成数据的破坏,不会冲突。所以它们进来读的时候,都给它们上读锁。* 但是一旦发现没有数据可读,就要有一个线程负责去读数据了,* 这时候它去数据库把数据读出来了,在它填充数据的时候就要上写锁。* 写之间不能被读走,所以这时候上写锁。* 数据填充完了,把写锁释放掉,再给它还原成为读锁。* 这样就读与写、写与写之间进行了互斥。又可以有多个并发的读。系统性能就很高了。*/private ReadWriteLock rwl = new ReentrantReadWriteLock();public Object getData(String key){rwl.readLock().lock();Object value = null;try{value = cache.get(key);if(value == null) {rwl.readLock().unlock(); //假如三个线程都走到这里了rwl.writeLock().lock(); //只有第一个线程可以进入,等这个线程将写锁释放掉,其它两个线程又进入。try{if(value==null){ //这里为了避免重复,加上一句判断。就算其它两个线程接着想来写的时候,它们发现这个数据不用再写了。value = "aaaa";//实际失去queryDB();}}finally{rwl.writeLock().unlock();}rwl.readLock().lock();}}finally{rwl.readLock().unlock();}return value;}
}
3.在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
package cn.itcast.heima;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*子线程循环10次,主线程循环10次,子线程再循环10次,主线程再循环10次,如此循环50次。*/public class ConditionCommunication {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {@Overridepublic void run() {for(int j=1; j <= 50; j++) {business.sub(j);}}}).start();for(int j=1; j <= 50; j++) {business.main(j);}}//静态方法不能创建内部类的实例,所以在类前加static。它的作用类似于外部类。它现在的完整名称是ConditionCommunication.Business。static class Business {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();private boolean bShouldSub = true;public void sub(int j) {lock.lock();try{while(!bShouldSub){try {condition.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("sub thread sequence of"+ i + ", loop of" + j);}bShouldSub = false;condition.signal();}finally{lock.unlock();}}public synchronized void main(int j) {lock.lock();try{while(bShouldSub){try {condition.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("main thread sequence of" + i+ ", loop of" + j);}bShouldSub = true;condition.signal();}finally{lock.unlock();}}}
}//本例中主要演示:Condition的await和signal方法。
//必须在synchronized同步监视器内部才可以用wait和notify。
4.一个锁内部可以有多个 Condition ,即有多路等待和通知,可以参看 jdk1.5 提供的 Lock 与 Condition 实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个 Condition ,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)
package cn.itcast.heima;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;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();}}
}
package cn.itcast.heima;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*三个线程,老大循环10次,老二循环10次,老三循环10次,再老大循环10次...如此循环50次。*/public class ThreeConditionCommunication {static Business business = new Business();public static void main(String[] args) {new Thread(new Runnable(){@Overridepublic void run() {for(int j=1; j <= 50; j++) {business.first(j);}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for(int j=1; j <= 50; j++) {business.second(j);}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for(int j=1; j<=50;j++) {business.third(j);}}}).start();}//静态方法不能创建内部类的实例,所以在类前加static。它的作用类似于外部类。它现在的完整名称是ConditionCommunication.Business。static class Business {Lock lock = new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();private int runptr = 1;public void first(int j) {lock.lock();try{while(runptr!=1){try {condition1.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("first thread sequence of"+ i + ", loop of" + j);}runptr = 2;condition2.signal();}finally{lock.unlock();}}public void second(int j) {lock.lock();try{while(runptr!=2){try {condition2.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for (int i = 1; i <= 10; i++) {System.out.println("second thread sequence of" + i+ ", loop of" + j);}runptr = 3;condition3.signal();}finally{lock.unlock();}}public void third(int j) {lock.lock();try{while(runptr!=3){try {condition3.await();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}for(int i=1;i<=10;i++){System.out.println("third thread sequence of" + i+ ", loop of" + j);}runptr = 1;condition1.signal();}finally{lock.unlock();}}}
}//老大只可以唤醒老二,老二只可以唤醒老三,老三只可以唤醒老大。
Semaphore实现信号灯
java.util.concurrent 类 Semaphore
构造方法摘要 | |
---|---|
Semaphore(int permits) 创建具有给定的许可数和非公平的公平设置的 Semaphore 。 | |
Semaphore(int permits, boolean fair) 创建具有给定的许可数和给定的公平设置的 Semaphore 。 |
方法摘要 | |
---|---|
void | acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。 |
int |
availablePermits() 返回此信号量中当前可用的许可数。 |
void |
release() 释放一个许可,将其返回给信号量。 |
1.Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
(1) Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。
package cn.itcast.heima;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;public class SemaphoreTest {public static void main(String[] args) {ExecutorService service = Executors.newCachedThreadPool();final Semaphore sp = new Semaphore(3);for(int i=0;i<10;i++){Runnable runnable = new Runnable(){@Overridepublic void run() {try {sp.acquire();//这里} catch (InterruptedException e1) {e1.printStackTrace();}System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-sp.availablePermits()) + "个并发");try {Thread.sleep((long)(Math.random()*10000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程" + Thread.currentThread().getName() + "即将离开"); sp.release();//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3-sp.availablePermits()) + "个并发"); }};service.execute(runnable);//此cached线程池,是往线程池里丢几个runnable,就开启几个线程。 }}}
运行结果:
线程pool-1-thread-2进入,当前已有3个并发
线程pool-1-thread-1进入,当前已有3个并发
线程pool-1-thread-3进入,当前已有3个并发
线程pool-1-thread-1即将离开
线程pool-1-thread-1已离开,当前已有2个并发
线程pool-1-thread-4进入,当前已有3个并发
线程pool-1-thread-4即将离开
线程pool-1-thread-4已离开,当前已有2个并发
线程pool-1-thread-5进入,当前已有3个并发
线程pool-1-thread-2即将离开
线程pool-1-thread-2已离开,当前已有2个并发
线程pool-1-thread-6进入,当前已有3个并发
线程pool-1-thread-3即将离开
线程pool-1-thread-3已离开,当前已有2个并发
线程pool-1-thread-7进入,当前已有3个并发
线程pool-1-thread-5即将离开
线程pool-1-thread-5已离开,当前已有2个并发
线程pool-1-thread-8进入,当前已有3个并发
线程pool-1-thread-6即将离开
线程pool-1-thread-6已离开,当前已有2个并发
线程pool-1-thread-9进入,当前已有3个并发
线程pool-1-thread-7即将离开
线程pool-1-thread-7已离开,当前已有2个并发
线程pool-1-thread-10进入,当前已有3个并发
线程pool-1-thread-9即将离开
线程pool-1-thread-9已离开,当前已有2个并发
线程pool-1-thread-8即将离开
线程pool-1-thread-8已离开,当前已有1个并发
线程pool-1-thread-10即将离开
线程pool-1-thread-10已离开,当前已有0个并发
(2) 另外等待的 5 个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造 Semaphore 对象时传入的参数选项。
2.单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
这篇关于黑马程序员---多线程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!