创建任务和线程
1,任务类实现implements接口,重写run方法;
public class TaskClass implements Runnable{ public TaskClass(){ // ... } public void run(){ //... } }
2, 使用Thread创建线程,Thread类包括创建线程的构造方法以及控制线程的方法;
TaskClass task new TaskClass();//创建任务 Thread thread = new Thread(task);//为任务创建线程
Thread类的方法
Thread() //创建一个空线程 Thread(Runnable task) //为指定任务创建一个线程 void start() //启动线程,使得run()方法被调用 boolean isAlive(int p) //j检测线程当前是否在运行 void setPriority() //设置线程的优先级p (范围从1到10) void join() //等到线程结束 例如:A线程调用B.join(),则A线程进入阻塞队列,等B线程运行完后,唤醒A线程 void sleep(long millis) //使线程睡眠指定毫秒 void yield() //当前线程暂停,进入就绪队列,运行其他线程
线程池
通过Thread来创建线程对单一任务的执行很方便,但需要为每个任务创建一个线程,对应大量任务来说是不够搞效的。因此java提供Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。
1,使用Executors类中的静态方法创建Executor对象
newFixedThreadPool(int)//创建固定数目的线程,当线程完成了任务的执行,它可以被重新使用以执行另外一个任务。当所有线程都不是空闲状态,而且有任务在等待,那么在关机前,如果因为错误终止了一个线程,就会创建一个新线程代替它。
newCachedThreadPool()//当有线程在等待时就创建新的线程,缓冲池中的线程的线程60秒没有使用就终止它。
Executor类的方法
execute(Runnable object)//接受任务 ExecutorService接口的方法 void shutdown() //关闭执行器,不接受新的任务,但要运行完已有的任务 list<Runnable> shutdownNow() //关闭执行器,即使还有任务没完成任务,返回没完成的任务清单 boolean isShutdown() //检测执行器是够已关闭 boolean isTerminated() //如果线程池所有任务都被终止,则返回true
1 import java.util.concurrent.*; 2 3 class PrintChar implements Runnable{ 4 char c; 5 int len; 6 public PrintChar(char a,int length) { 7 c=a; 8 len=length; 9 } 10 @Override 11 public void run() { 12 // TODO 自动生成的方法存根 13 for(int i=0;i<len;i++) { 14 System.out.print(c); 15 } 16 } 17 18 } 19 20 public class ExecutorDemo { 21 public static void main(String args[]) { 22 // ExecutorService executor = Executors.newFixedThreadPool(1); 23 ExecutorService executor = Executors.newCachedThreadPool(); 24 executor.execute(new PrintChar('a',100)); 25 executor.execute(new PrintChar('b',100)); 26 executor.execute(new PrintChar('c',100)); 27 28 executor.shutdown(); 29 } 30 }
线程同步
synchronized 同步的方法在执行前会隐式的加上一个锁:方法锁,对象锁 和 类锁
1,给方法加synchronized关键字
1)给普通方法加:当运行到这个方法时,实际是给调用这个方法的对象加锁,其他线程调用这个对象的这个方法时就会阻塞;
2)给静态方法加:当运行到这个方法时,实际是给调用这个方法的类加锁,其他线程调用这个类的这个方法时就会阻塞;
public synchronized void deposit(double amount)//当运行到这里调用这个方法的时候,实际上是给调用这个方法的对象加锁了
public synchronized static void deposit(double amount)//当运行到这里调用这个方法的时候,实际上是给这个方法的类加锁了
2, 给同步块加锁:可以给任何对象加锁,只会同步部分代码,而不必同步整个方法,大大增强了程序的并发能力;
1)对调用这个方法的对象加锁
public void acount(){ synchronized (this){//还可写其他对象的名字,表示对其他对象加锁 ...... } }
2)给这个方法的类加锁
public void acount(){ synchronized (acount.class){//方法.class 可以给这个方法的所属类加锁 ...... } }
java显式的加锁:显式加锁对线程的控制更加直观灵活,重要的是可以进行线程间的协作
通过ReentrantLock类可以实现接口Lock来表示一个锁,ReentrantLock是创建相互排斥的锁的Lock的具体表现。
创建Lock锁,获取锁,释放锁
Lock lock =new ReentrantLock();//创建锁
lock.lock();//获取锁
lock.unlock();//释放锁
线程间协作
调用Lock对象的newCondition()方法创建的对象可以使用await(),signal(),signalAll()方法来实现线程之间的互相通信。
Lock lock = new ReentrantLock(); Condition newDeposit = lock.newCondition(); newDeposit.await();//当前线程等待,直到被signal()或signalAll()唤醒 newDeposit.signal();//唤醒一个等待线程 newDeposit.signalAll();//唤醒所以等待线程
示例学习:生产者/消费者
package test; import java.util.concurrent.*; import java.util.concurrent.locks.*; public class ConsumerProducer { public static Buffer buffer=new Buffer(); public static void main(String args[]) { ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new ProducerTack()); executor.execute(new ConsumerTask()); executor.shutdown(); } private static class ProducerTack implements Runnable{ public ProducerTack() { // TODO 自动生成的构造函数存根 } @Override public void run() { try { int i=1; while(true) { System.out.println("Producer writes" + i); buffer.write(i++); Thread.sleep((int)(Math.random()*10000)); } }catch(InterruptedException ex) { ex.printStackTrace(); } } } private static class ConsumerTask implements Runnable{ public ConsumerTask() { // TODO 自动生成的构造函数存根 } public void run() { try { while(true) { System.out.println(" Consumer reads " + buffer.read()); Thread.sleep((int)(Math.random()*10000)); } }catch(InterruptedException ex) { ex.printStackTrace(); } } } private static class Buffer{ private static final int CAPACITY=1; private java.util.LinkedList<Integer> queue=new java.util.LinkedList<Integer>(); private static Lock lock=new ReentrantLock(); private static Condition notEmpty=lock.newCondition(); private static Condition notFull = lock.newCondition(); public Buffer() { // TODO 自动生成的构造函数存根 } public void write(int value) { lock.lock(); try { while(queue.size()==CAPACITY) { System.out.println("Wait for notFull condition"); notFull.await(); } queue.offer(value); notEmpty.signal(); } catch(InterruptedException ex){ ex.printStackTrace(); }finally { lock.unlock(); } } public int read() { int value=0; lock.lock(); try { while(queue.isEmpty()) { System.out.println(" Wait for notEmpty condition"); notEmpty.await(); } value=queue.remove(); notFull.signal(); }catch(InterruptedException ex) { ex.printStackTrace(); }finally { lock.unlock(); } return value; } } }
阻塞队列
java.util.concurrent包中提三个具体的阻塞队列ArrayBlockingQueue,LinkedBlockingQueue和PriorityBlockingQueue;
阻塞队列的方法:
put(element e) //在队尾插入一个元素,如果队列已满则线程进入阻塞队列等待。
take() //返回并删除这个队列的头,如果队列为空则线程进入阻塞队列等待。
LinkedBlockingQueue是可以创建不受限的或受限的链队,对于不受限队列而言,put方法将永远不会阻塞。
例子:用阻塞队列改写上面的生产者/消费者
package test; import java.util.concurrent.*; import java.util.concurrent.locks.*; public class ConsumerProducer { // public static Buffer buffer=new Buffer(); private static ArrayBlockingQueue<Integer> buffer =new ArrayBlockingQueue<Integer>(1); public static void main(String args[]) { ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new ProducerTack()); executor.execute(new ConsumerTask()); executor.shutdown(); } private static class ProducerTack implements Runnable{ public ProducerTack() { // TODO 自动生成的构造函数存根 } @Override public void run() { try { int i=1; while(true) { buffer.put(i); System.out.println("Producer writes" + i); i++; Thread.sleep((int)(Math.random()*1000)); } }catch(InterruptedException ex) { ex.printStackTrace(); } } } private static class ConsumerTask implements Runnable{ public ConsumerTask() { // TODO 自动生成的构造函数存根 } public void run() { try { while(true) { System.out.println(" Consumer reads " + buffer.take()); Thread.sleep((int)(Math.random()*1000)); } }catch(InterruptedException ex) { ex.printStackTrace(); } } } }
信号量Semaphore
信号量用来限制访问共享资源的线程数,在访问资源之前,线程必须从信号量获取许可,在访问完资源之后,这个线程必须将许可返回给信号量。
构造方法:Semaphore(int numberOfPermits int) //创建一个带指定数目许可的信号量,公平策略为false
方法: void acquire() //获取这个信号量的许可。如果无许可可用,线程就被锁住,直到有可用许可为止
void release() //释放一个许可给该信号量
避免死锁
给资源安排一个加锁顺序,就不会发生死锁的现象了
同步集合
Java集合框架中的类不是线程安全的,可以通过锁定集合和同步集合保护集合中数据。
Collections类提供六个静态方法来将集合转成同步版本,这些方法创建的集合成为同步包装类
方法:
Collection synchronizedCollection(Collection c) //返回同步集合
List synchronizedList(List list) //返回同步线性表
Map synchronizedMap(Map m) //返回同步图
Set synchronizedSet(Set s) //返回同步规则集
SortedMap synchronizedSortedMap(SortedMap s) //返回同步有序图
SortedSet synchronizedSortedSet(SortedSet c) //返回同步有序规则集
同步集合可以很安全地被多个线程并发的访问和修改
注意:如果用迭代器对集合进行遍历时,同时对集合进行修改,迭代器就会抛出异常java.util.ConcurrentModificationExceptione而结束;
为了避免这个错误,需要创建一个同步集合对象,并且在遍历它时获取对象上的锁。
Set hashSet = Collections.synchronizedSet(new HashSet());//同步集合 synchronized (hashSet){//加锁 Iterator iterator = hashset.iterator(); while(iterator.hasnext()){ System.out.println(iterator.next()); } }