线程Thread

基本概念: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包中,里面有三个重要的接口:

  •  Condition:将Object监视器方法(wait、notify、notifyAll)分解成大相径庭的对象,以便经过将这些对象与任意Lock实现组合适用,为每一个对象提供多个等待。
  •  Lock:提供了比使用synchronized方法和语句更普遍的锁定操做。
  •  ReadWriteLock:维护了一组相关的锁定,一个只读操做,一个写入操做。

 

直接提供一段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();
		}
	}
}
相关文章
相关标签/搜索