博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程Thread
阅读量:6550 次
发布时间:2019-06-24

本文共 11876 字,大约阅读时间需要 39 分钟。

hot3.png

基本概念:

       线程是一个程序内部的顺序控制流,一个进程相当于一个任务,一个线程相当于任务的一个执行路径。

       进程是一个操作系统执行的任务,一般都是.exe文件。一个进程中可以运行着多个线程。

线程和进程的相似性在于它们都是单一顺序控制流。

       多进程就是一个操作系统运行着多个任务。

       多线程就是一个程序内部运行着多个顺序控制流。

       每个Java运行程序至少有一个主线程,例如:public static void main(String[] args){}就是一个主线程。

 

运行方式:

       通过start()方法启动一个线程。

       通过run()来执行一个线程,它是线程的主体部分。

       通过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);//使用延迟,隔1、3、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
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();		}	}}

 

转载于:https://my.oschina.net/u/734885/blog/889872

你可能感兴趣的文章
int.NSInteger.NSUInteger.NSNumber的区别
查看>>
使用JS与CSS3的翻转实现3D翻牌效果
查看>>
上海期货交易所CTP行情和交易接入
查看>>
Django models 操作高级补充
查看>>
springClould 的一些组件
查看>>
验证码
查看>>
Spring扩展:Spring框架的由来
查看>>
[2017BUAA软工助教]博客格式的详细说明
查看>>
PHP面向过程和面向对象
查看>>
使用Visual Studio2012调试Redis源码
查看>>
jQuery中slideToggle()的详细使用方法和解释
查看>>
微信支付开发(4) 交易通知
查看>>
为什么我的淘宝店铺动态评分清零了?
查看>>
Python web前端 06 运算符 循环
查看>>
关于SQL 导出脚本失败 及SQL 的重装
查看>>
VFP数据库主要命令的使用总结
查看>>
[Andriod官方训练教程]管理Activity的生命活动之开始一个Activity
查看>>
自定义Navigation按钮及Title
查看>>
Carmark算法,我的实现
查看>>
js获取当前日期时间“yyyy-MM-dd HH:MM:SS”
查看>>