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

---------------------- 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培训、期待与您交流! ----------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值