黑马程序员----java之多线程

原创 2013年12月03日 16:02:19
---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------


多线程对于java编程是非常重要的,并且在面试过程中被问到的几率也比较大,所以对多线程知识进行总结就很必要了。

进程与线程

当一个程序进入内存运行,即变成一个进程。内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。

进程的特点:独立性,动态性,并发性。
多线程来实现并发比使用多进程并发的性能要高得多:当操作系统创建一个进程时,必须为进程分配独立的内存空间,并分配大量相关资源;多个线程将共享同一个进程虚拟空间。


多线程的创建方式

继承Thread类创建线程
实现Runnable接口创建线程;
实现Callable接口创建线程。(java jdk1.5新增)

Callable和Future:
Callable接口也提供了一个call()方法可以作为线程执行体,但call方法比run()方法功能更强大:
call()方法可以有返回值。  call()可以声明抛出异常。

Future接口用来代表Callable接口里call()方法的返回值。

但是大多数都是采用Runnable方式创建多线程,因为unnable接口方式创建多线程,线程类只是实现了Runnable接口,还可以继承其他类,并且可以多个线程共享一个target对象,所以非常适合多个相同线程来处理一份资源的情况,从而可以将cpu,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。


线程的生命周期:新建、就绪、运行、阻塞、死亡。
当发生如下情况下,线程将会进入阻塞状态:
线程调用sleep方法主动放弃所占用的处理器资源。
线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞。
线程视图获得一个同步监视器。但该同步监视器正被其他线程所持有。
线程在等待某个通知。
程序调用了线程的suspend方法将该线程挂起。

可以调用yield()方法从运行状态到就绪状态。
测试某条线程是否已经死亡,可以调用线程对象的isAlive()方法,线程出入就绪,运行,阻塞三种状态,返回true.
处于新建、死亡状态时,返回false.

线程控制

join():当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止。

后台线程:在后台运行的,它的任务是为其他的线程提供服务。jvm的垃圾回收线程就是典型的后台线程。
使用setDaemon(true)方法可将指定线程设置成后台线程。必须在start前设置。

线程睡眠:sleep
当当前线程调用sleep方法进入阻塞状态后,在起sleep时间段内,该线程不会获得执行的机会,即使系统中没有其他科运行的线程,处于sleep中的线程也不会运行,因此 sleep方法常用来暂定程序的执行。

线程让步:yield
只会给优先级相同,或优先级更高的线程执行机会。

sleep和yield的区别:
sleep方法暂停当前线程后,会给其他线程执行机会,但yield方法只会给优先级相同或更高的线程执行机会。
sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态。而yield只是强制当前线程进入就绪状态。

改变线程优先级:
Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回指定线程优先级。
MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5


线程的同步
线程安全问题--模拟取钱
同步代码块  synchronized(obj){....}  obj-同步监视器。
同步方法  同步方法就是使用synchronized关键字来修饰某个方法。
同步锁  Lock是控制多个线程对共享资源进行访问的工具。 
死锁   当两个线程相互等等对方释放同步监视器时就会发生死锁。

下面是模拟取钱的例子,用同步代码块和同步方法解决,注释掉的是同步代码块方式。

package com.thread;
public class Account {
	private String accountNo;//编号
	private double balance;//余额
	public Account(String accountNo,double balance){
		this.accountNo=accountNo;
		this.balance=balance;
	}
	public String getAccountNo() {
		return accountNo;
	}
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	public double getBalance() {
		return balance;
	}
	/*
	public void setBalance(double balance) {
		this.balance = balance;
	}*/
	public int hashCode() {
		return accountNo.hashCode();
	}
	public boolean equals(Object obj) {
		if(obj!=null&&obj.getClass()==Account.class){
			Account target=(Account)obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
	public synchronized void draw(double drawAmount){//采用同步方法进行同步
		if(balance>drawAmount){
			System.out.println(Thread.currentThread().getName()+"取钱成功,吐出钞票:"+drawAmount);
			try {
				Thread.sleep(1);
			} catch (Exception e) {
				e.printStackTrace();
			}
			balance=balance-drawAmount;
			System.out.println("余额为:"+balance);
		}else{
			System.out.println(Thread.currentThread().getName()+"取钱失败,余额不足!");
		}
	}
}
package com.thread;
public class DrawThread extends Thread{
	private Account account;
	private double drawAmount;//取款数额
	public DrawThread(String name,Account account,double drawAmount){
		super(name);
		this.account=account;
		this.drawAmount=drawAmount;
	}
	/*
	@Override
	public void run() {
		synchronized (account) {//采用同步代码块的方式同步
			if(account.getBalance()>drawAmount){
				System.out.println(getName()+"取钱成功,吐出钞票:"+drawAmount);
				try {
					Thread.sleep(1);
				} catch (Exception e) {
					e.printStackTrace();
				}
				account.setBalance(account.getBalance()-drawAmount);
				System.out.println("余额为:"+account.getBalance());
			}else{
				System.out.println(getName()+"取钱失败,余额不足!");
			}
		}
	}
	*/
	@Override
	public void run() {//采用同步方法后的调用
		account.draw(drawAmount);
	}
}

package com.thread;
public class TestDraw {
	public static void main(String[] args) {
		Account xia=new Account("123456",1000);
		new DrawThread("甲",xia,800).start();
		new DrawThread("乙",xia,800).start();
	}
}


线程通信

线程的协调运行  wait():等待   notify():唤醒   notifyAll():唤醒所有(对于使用synchronized关键字来保证同步)
使用条件变量控制协调  await();signal();signalAll();(用于Lock锁中)
private final Lock lock=new ReentrantLock();
private final Condition cond=lock.newCondition();

使用管道流  
管道字节流:PipedInputStream,PipedOutputStream.
管道字符流:PipedReader,PipedWriter
新IO的管道Channel:Pipe.SinkChannel,Pipe.SourceChannel

管道流的示例代码:

package com.thread;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;

class ReaderThread extends Thread{
	private PipedReader pr;
	private BufferedReader br;
	public ReaderThread(){}
	public ReaderThread(PipedReader pr){
		this.pr=pr;
		this.br=new BufferedReader(pr);
	}
	public void run() {
		String buf=null;
		try {
			while((buf=br.readLine())!=null){
				System.out.println(buf);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
				try {
					if(br!=null){
						br.close();
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}		
		}
	}	
}


class WriterThread extends Thread{
	String books[]=new String[]{"疯狂java讲义","疯狂Android讲义","疯狂Ajax讲义"};
	private PipedWriter pw;
	public WriterThread(){}
	public WriterThread(PipedWriter pw){
		this.pw=pw;
	}
	public void run() {
		try{
			for(int i=0;i<100;i++){
				pw.write(books[i%3]+"\n");
			}
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			try {
				if(pw!=null){
					pw.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}	
	}
}

public class PipedCommunicationTest {
	public static void main(String[] args) {
		PipedReader pr=null;
		PipedWriter pw=null;
		try {
			pr=new PipedReader();
			pw=new PipedWriter();
			pw.connect(pr);
			new WriterThread(pw).start();
			new ReaderThread(pr).start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}


线程池

jdk1.5提供了一个Executors工厂类来产生连接池,该工厂类里包含如下几个静态工厂方法来创建连接池:
(一共5个,还各有一个固定只创建一个线程数的不写了)
newCachedThreadPool():创建一个具有缓存功能的线程池。
newFixedThreadPool(int nThreads);创建一个可重用的、具有固定线程数的线程池。
---返回一个ExecutorService对象。
newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟执行线程任务。 ---返回一个ScheduledExecutorService线程池。
步骤:调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。
创建Runnable实现类作为任务,
调用ExecutorService对象的submit方法来提交Runnable实例。
调用ExecutorService对象的shutdown方法来关闭线程池。


线程相关类

线程相关类
ThreadLocal类:为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
提供3个public方法:T get():返回此线程局部变量中当前线程副本中的值。
void remove():删除此线程局部变量中当前线程的值。
void set(T value):设置此线程局部变量中当前线程副本中的值。

ThreadLocal和其他所有的同步机制都是为了解决多线程中对同一变量的访问冲突。
同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上避免了多个线程之间共享资源。
因此:如果需要进行过个线程之间的共享资源,已达到线程之间的通信功能,就使用同步机制;
     如果仅仅需要隔离多个线程之间的共享冲突,可以使用ThreadLocal.





---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

Java 7之多线程第8篇 - 互斥锁 ReentrantLock

ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。也就是说ReentrantLock在同一个时间点只能被一个线程获取。 Java的synchronized块并不保证尝试进入它们的线程...
  • mazhimazh
  • mazhimazh
  • 2014年02月13日 14:59
  • 2865

Android-Service系列之断点续传下载

PS:本文代码来自慕课网同名课程Android-Service系列之断点续传下载,这里只是为了Mark下来方便自己以后使用。 首先是布局文件,因为只是demo,所以使用图形化工具生成。 ...
  • u013983998
  • u013983998
  • 2015年06月03日 23:33
  • 1179

【Java】Java之多线程

Java多线程是什么? Java提供的并发(同时、独立)处理多个任务的机制。多个线程共存于同一JVM进程里面,所以共用相同的内存空间,较之多进程,多线程之间的通信更轻量级。依我的理解,Java多线程...
  • mrlin6688
  • mrlin6688
  • 2015年11月21日 10:40
  • 376

Java之多线程

------- android培训、java培训、期待与您交流! ---------- 面向对象——多线程 day12 01-多线程-概述 1、线程与进程          整个区域叫做进程。进程是...
  • u013114489
  • u013114489
  • 2015年07月22日 12:34
  • 197

Java回炉之多线程(一)

Java回炉之多线程Java回炉之多线程 创建方法 设置获取线程名 获取当前线程 优先级 线程控制 生命周期 状态转换 同步代码块 同步方法 Lock锁 生产者消费者 线程组 线程池 定时器创建方法 ...
  • xmh19936688
  • xmh19936688
  • 2015年12月09日 23:34
  • 1357

Java之多线程

两种方式继承Thread类:class MyThread extends Thread { @Override public void run() { // Java语句...
  • Ro_bot
  • Ro_bot
  • 2016年07月09日 21:26
  • 279

Java之多线程、线程池

多线程 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少一个线程(main),但是可以有多个线程的,则这个程序称为多线程程序。每一个线程执行不同的任务,多线程程序并不能提高程...
  • xiaoyuxianshenging
  • xiaoyuxianshenging
  • 2017年07月03日 22:08
  • 148

Java之多线程

哎呀呀,自从在科室做的都是偏C方向的,Java已经两年没碰了,基本都丢的差不多精光了。正好目前趁着假期再拾起来,Java水平估计连入门都不算,欢迎大神尽情吐槽! 谈到多线程,就一定要理解,进程和线程...
  • SkySuperWL
  • SkySuperWL
  • 2016年08月07日 11:07
  • 404

Java基础之多线程

1、进程和线程:     进程:正在进行的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。     线程:进程内部的一条执行路径或者一个控制单元。     两者的...
  • StevenXu2012
  • StevenXu2012
  • 2014年11月18日 13:45
  • 255

Java之多线程基础

第1章多线程1.1多线程介绍学习多线程之前,我们先要了解几个关于多线程有关的概念。进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一...
  • lwl2014100338
  • lwl2014100338
  • 2017年12月16日 20:53
  • 41
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:黑马程序员----java之多线程
举报原因:
原因补充:

(最多只允许输入30个字)