基本概念:java
线程是一个程序内部的顺序控制流,一个进程至关于一个任务,一个线程至关于任务的一个执行路径。缓存
进程是一个操做系统执行的任务,通常都是.exe文件。一个进程中能够运行着多个线程。服务器
线程和进程的类似性在于它们都是单一顺序控制流。多线程
多进程就是一个操做系统运行着多个任务。并发
多线程就是一个程序内部运行着多个顺序控制流。dom
每一个Java运行程序至少有一个主线程,例如:public static void main(String[] args){}就是一个主线程。ide
运行方式:函数
经过start()方法启动一个线程。工具
经过run()来执行一个线程,它是线程的主体部分。this
经过sleep(long millis)方法来使当前线程休眠一段时间,当过了mills时间后再恢复到可运行态,不是运行状态。所以,sleep()方法不能保证该线程到期后就立马开始运行。sleep()是静态方法,只能控制当前正在运行的线程,所以,休眠期间不影响其余线程的运行。
经过yield()方法暂停当前执行的线程,让同优先级的线程轮询执行一段时间。
经过手动调用stop()方法来结束一个线程(或者执行到run()方法的末尾,或者抛出未经处理Exception/Error,这两都是程序自动结束线程)。
经过join()方法来让线程A加入到线程B的尾部,在B执行完以前A不能工做。
经过notify() 唤醒在此对象监视器上等待的单个线程。
经过notifyAll() 唤醒在此对象监视器上等待的全部线程。
经过wait() 致使当前的线程等待,直到其余线程调用此对象的 notify()方法或 notifyAll()方法。
如何建立和启动线程:
两种方式:extends Thread类、implements Runnable接口。
Thread类:也是实现了Runnable接口。调用方式:继承Thread类后的子类生成对象后调用start()方法,如:new MyThread().start()。
class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("i="+i); } } }
Runnable接口:只有一个run()方法来定义线程运行体。使用Runnable接口能够为多线程提供共享数据。调用方式:将子类对象经过Thread的构造器来执行start()方法,如:new Thread(new MyRunnable ()).start()。
class MyRunnable implements Runnable{ String name ="xiaom"; @Override public void run() { System.out.println("name:"+name); } }
线程状态:
① 新线程态(New Thread)
产生了一个Thread对象就生成一个新线程。当线程处于“新线程”状态时,仅仅只是一个空线程对象,系统尚未给它分配资源。所以,只能start()或者stop()操做,除此以外的操做都会引起异常。
② 可运行态(Runnable)
start()方法产生线程运行时所需的资源,调度线程执行,而且调用run()方法时候,线程处于“可运行”状态。之因此不称为“运行态”是由于它不老是一直占用处理器。特别是对只有一个处理器的PC而言,任什么时候刻只有一个可运行状态的线程占用。Java经过调度来实现多线程对处理器的共享。
③ 非运行态(Not Runnable)
当sleep()、wait()等方法被调用或者线程出于I/O等待时称为“非运行”状态。
④ 死亡态(Dead)
当run()方法返回,或者别的线程调用stop()方法时,线程进入“死亡”状态。
线程优先级:
当PC只有一个处理器时,以某种顺序在单处理器的状况下执行多线程被称为”调度”。Java采用的是固定优先级调度,根据处于可运行状态线程的优先级来调度。
当线程产生时,它继承原线程的优先级,必要的时候能够经过Thread.setPriority()方法对优先级更改。若是有多个线程等待运行,系统选择优先级别最高的可运行线程运行。只有当它中止、自动放弃、或者因为某种缘由成为非运行态时低优先级的线程才能运行。若是两个线程具备相同的优先级,那么它们会被交替运行。
在任什么时候刻,若是一个比其余线程优先级都高的线程的状态变为“可运行“,那么实时系统将选择该线程来运行。
线程组:
每一个Java线程都是某个线程组的成员。线程组提供一种机制,将多个线程集于一个对象内,能对它们进行总体操做。譬如,你能用一个方法调用来启动或挂起组内的全部线程。Java线程组由ThreadGroup类实现。当线程产生时,能够指定线程组或由实时系统将其放入某个缺省的线程组内。线程只能属于一个线程组,而且当线程产生后不能改变它所属的线程组。
多线程同步:
由于并发性,当多个线程同时操做一个可共享资源时易产生数据不一致,所以加入同步锁来避免该线程没有完成操做以前有其余线程进入,从而保证数据的惟一性和准确性。
使用synchronized关键字修饰。
① 修饰方法:
private synchronized void put(){}
Java中每一个对象都有一个内置锁,用此关键字修饰方法时,内置锁会保护整个方法。在调用方法前,须要得到内置锁,不然会形成阻塞。
synchronized还能够修饰static方法,当静态方法被调用时,锁住的就是整个Class。
//修饰方法 private synchronized void _outTicket(){ if(total>0){ System.out.println(Thread.currentThread().getName()+ "出票" + this.total); this.total--; } }
② 修饰代码块:
synchronized(this){}
被关键字修饰的语句块会自动加上内置锁,从而实现同步。
class MoreThread implements Runnable { private int total = 10;// 总票数 @Override public void run() { for (int i = 0; i < 20; i++) {//线程运行数 try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } outTicket(); } } // 出票方法 private void outTicket() { synchronized(this){//修饰代码块 if(total>0){ System.out.println(Thread.currentThread().getName()+ "出票" + this.total); this.total--; } } } } public class MyThreadTest { public static void main(String[] args) { // 多线程同步 MoreThread t = new MoreThread(); new Thread(t,"线程1").start(); new Thread(t,"线程2").start(); new Thread(t,"线程3").start(); } }
同步是一种高开销的操做,所以应该尽可能减小同步内容。一般不必同步整个方法,使用synchronized代码块同步关键代码便可。
线程锁:
① 原理:
Java中每一个对象都有一个内置锁。当程序运行到synchronized修饰的非静态方法上时会自动得到当前执行代码的对象的锁。
一个对象只有一个锁,若是一个线程得到当前对象锁,其余线程就不能再得到该对象锁,除非该锁已被释放。
释放锁是指持锁对象已退出了synchronized修饰的方法或者代码块。
② 锁和同步的要点:
a) 只能同步方法,不能同步变量和类。
b) 没必要同步类的全部方法,类能够同时拥有同步方法合非同步方法。
c) 若是两个线程要执行一个类中的synchronized方法,而且两个线程使用相同的实例来调用方法,那么一次只能有一个线程可以执行方法,另外一个须要等待,直到锁被释放。也就是说:若是一个线程在对象上得到一个锁,就没有任何其余线程能够进入(该对象的)类中的任何一个同步方法。
d) 若是线程拥有同步和非同步方法,则非同步方法能够被多个线程自由访问而不受锁的限制。
e) 线程睡眠时,锁不会释放。
f) 线程能够得到多个锁。好比,在一个对象的同步方法里面调用另一个对象的同步方法,则获取了两个对象的同步锁。
向线程传递数据:
① 经过构造器传递
class MyRunnable implements Runnable { String name ; MyRunnable(String name){ this.name = name; } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("name:" + name+i); } } }
调用方式:
new Thread(new MyRunnable("Xiaoming")).start();
②经过变量的setters方法传递
class MyRunnable implements Runnable { String name ; public void setName(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("name:" + name+i); } } }
调用方式:
MyRunnable myRunnable = new MyRunnable(); myRunnable.setName("Xiaoming"); Thread thread = new Thread(myRunnable); thread.start();
③ 经过回调函数传递
class Data { public int i =100;//初始数据 } class Work{ public void process(Data data, int random){ System.out.println("data.i="+data.i+" random="+random); data.i*=random; } } class NewThread implements Runnable{ private Work work; NewThread(Work work){ this.work = work; } @Override public void run() { Data data = new Data(); work.process(data, new Random().nextInt(10));//回调函数 System.out.println("result:"+data.i); } public static void main(String[] args) { new Thread(new NewThread(new Work())).start(); } }
线程池:
线程池做用就是限制系统中执行线程的数量。
根据系统的环境状况,能够自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了形成系统拥挤效率不高。用线程池控制线程数量,其余线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务须要运行时,若是线程池中有等待的工做线程,就能够开始运行了;不然进入等待队列。
① 减小建立和销毁线程的次数,每一个工做线程均可以被重复利用,可执行多个任务。
② 可根据系统的承受能力,调整线程池中线程的数目,防止由于消耗过多的内存,而把服务器累趴(每一个线程大约须要1MB内存,线程开的越多内存消耗就越大)。
池的建立方式:
Java里面线程池的顶级接口是Executor,可是严格意义上讲Executor并非一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
Executors提供如下几个方法来建立线程池:
① newFixedThreadPool(int nThreads):建立一个可重用固定线程数的线程池。
代码:
ExecutorService pool =Executors.newFixedThreadPool(3); pool.execute(new Thread(new MoreThread(),"线程1")); pool.execute(new Thread(new MoreThread(),"线程2")); pool.execute(new Thread(new MoreThread(),"线程3")); //关闭线程池 pool.shutdown();
② newSingleThreadExecutor():建立一个单线程的线程池。线程池只有一个线程在工做,若是这个线程出现异常,会有一个新的线程来替代它。此线程池保证全部任务的执行顺序按照任务提交的顺序执行。
代码:
ExecutorService pool = Executors.newSingleThreadExecutor();
③ new CachedThreadPool():建立一个可缓存的线程池。若是线程池的大小超过了处理任务所须要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增长时,此线程池又能够智能的添加新线程来处理任务。此线程池不会对线程池大小作限制,线程池大小彻底依赖于操做系统(或者说JVM)可以建立的最大线程大小。
代码:
ExecutorService pool = Executors.newCachedThreadPool();
④ newScheduledThreadPool(int corePoolSize):建立一个线程池,它可安排在给定延迟后运行命令或者按期的执行。
代码:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1); //使用延迟,隔一、三、5秒执行当前线程 pool.schedule(new Thread(new MoreThread(),"线程1"), 1000, TimeUnit.MILLISECONDS); pool.schedule(new Thread(new MoreThread(),"线程2"), 3000, TimeUnit.MILLISECONDS); pool.schedule(new Thread(new MoreThread(),"线程3"), 5000, TimeUnit.MILLISECONDS); //关闭线程池 pool.shutdown();
自定义线程池ThreadPoolExecutor:
ThreadPoolExecutor的构造器:
ThreadPoolExecutor(int corePoolSize ,int maximumPoolSize ,long keepAliveTime ,TimeUnit unit ,BlockingQueue<Runnable> workQueue ,ThreadFactory threadFactory ,RejectedExecutionHandler handler);
参数:
corePoolSize:池中所保存的线程数,包括空闲线程。
maximumPoolSize:池中容许的最大线程数。
keepAliveTime:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit:keepAliveTime的时间单位。
workQueue:执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。
threadFactory:执行程序建立新线程时使用的factory。
handler:因为超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
并发机制-锁:
在Java5中,专门提供了锁对象,利用锁能够方便的实现资源的封锁,利用控制对竞争资源并发访问的控制,这些内容主要在java.util.concurrent.locks包中,里面有三个重要的接口:
直接提供一段Lock代码:
/** * 线程锁的机制 * @author admin */ public class ThreadLockTest { public static void main(String[] args) { Ticket ticket = new Ticket(30);//建立总票数 TicketWindow wind = new TicketWindow();//售票窗口随机 Lock lock = new ReentrantLock();//建立锁 ExecutorService pool = Executors.newCachedThreadPool();//线程池 pool.execute(new Thread(new Tecketing(wind, new Buyyer("张力",2), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("李兴",1), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("刘兰兰",5), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("兔兔",2), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("张力",2), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("姚丽丽",1), ticket, lock))); pool.execute(new Thread(new Tecketing(wind, new Buyyer("姚明明",3), ticket, lock))); pool.shutdown(); } } /** * 售票窗口类 * @author admin */ class TicketWindow{ //随机返回一个窗口 public int wind(){ return (new Random().nextInt(5)+1); } } /** * 总票数类 * @author admin */ class Ticket{ private int total; //构造器 public void setTotal(int total) { this.total = total; } public Ticket(int total) { this.total = total; } public int getTotal() { return total; } } /** * 售票机制 * @author admin */ class Tecketing implements Runnable{ private TicketWindow wind; //窗口 private Buyyer user; private Ticket ticket; //总票数 private Lock lock; public Tecketing(TicketWindow wind, Buyyer user, Ticket ticket, Lock lock) { this.wind = wind; this.user = user; this.ticket = ticket; this.lock = lock; } @Override public void run() { //获取锁 lock.lock(); if(ticket.getTotal()>0){ System.out.print(user.getName()+"在"+wind.wind()+"窗口购买了"+user.getTicketCount()+"张票,"); ticket.setTotal(ticket.getTotal()-user.getTicketCount()); System.out.println("目前剩余票数:"+ticket.getTotal()); }else{ System.out.println("今日票已所有售罄!"); } //释放锁,不然其余线程没法执行 lock.unlock(); } } /** * 购票者类 */ class Buyyer{ private String name; private int ticketCount; //购票数量 public Buyyer(String name, int ticketCount) { this.name = name; this.ticketCount = ticketCount; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getTicketCount() { return ticketCount; } public void setTicketCount(int ticketCount) { this.ticketCount = ticketCount; } }
ReadWriteLock机制的锁,读写锁分离,灵活性更好。代码:
/** * ReadWriterLock接口读写时分别用锁 * @author admin */ public class ReadWriterLokTest { public static void main(String[] args) { Ticket ticket = new Ticket(30);//建立总票数 TicketWindow wind = new TicketWindow();//售票窗口随机 ReadWriteLock lock = new ReentrantReadWriteLock();//建立锁 ExecutorService pool = Executors.newCachedThreadPool();//线程池 pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("窗口",0), ticket, lock, false))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("张力",2), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("李兴",1), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("刘兰兰",5), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("兔兔",2), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("张力",2), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("姚丽丽",1), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("姚明明",3), ticket, lock, true))); pool.execute(new Thread(new Tecketing_1(wind, new Buyyer("窗口",0), ticket, lock, false))); pool.shutdown(); } } /** * 售票机制 * @author admin */ class Tecketing_1 implements Runnable{ private TicketWindow wind; //窗口 private Buyyer user; private Ticket ticket; //总票数 private ReadWriteLock lock; private boolean isCheck;//是否查询 private int solded =0;//已售卖 public Tecketing_1(TicketWindow wind, Buyyer user, Ticket ticket, ReadWriteLock lock,boolean isCheck) { this.wind = wind; this.user = user; this.ticket = ticket; this.lock = lock; this.isCheck = isCheck; } @Override public void run() { if(isCheck){ //获取写锁 lock.writeLock().lock(); if(ticket.getTotal()>0){ System.out.print(user.getName()+"在"+wind.wind()+"窗口购买了"+user.getTicketCount()+"张票,"); ticket.setTotal(ticket.getTotal()-user.getTicketCount()); System.out.println("目前剩余票数:"+ticket.getTotal()); }else{ System.out.println("今日票已所有售罄!"); } //释放 lock.writeLock().unlock(); }else{ //获取读锁 lock.readLock().lock(); solded +=user.getTicketCount(); ticket.setTotal(ticket.getTotal()-solded); System.out.println("窗口"+wind.wind()+" 正在查询剩余票数:"+ticket.getTotal()); //释放 lock.readLock().unlock(); } } }