多线程的使用

继承Thread类创建线程类

public class FirstThread extends Thread
{
	private int i=0;
	public void run(){
		for(;i<100;i++){
			System.out.println(getName()+"#"+i);
		}
	}
	public static void main(String[] args){
		for(int i=0;i<100;i++){

			System.out.println(Thread.currentThread().getName());   
			if(i==20){
				new FirstThread().start();     //创建线程,创建的是一个单独的实例不共享类的变量
				new FirstThread().start();    //创建线程
			}
		}
	}
}

通过Runable接口创建线程类



/**
 * Description:
 * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
 * <br/>Copyright (C), 2001-2016, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author Yeeku.H.Lee kongyeeku@163.com
 * @version 1.0
 */
// 通过实现Runnable接口来创建线程类
public class SecondThread implements Runnable
{
	private int i ;
	// run方法同样是线程执行体
	public void run()    //同样是线程的执行体
	{
		for ( ; i < 100 ; i++ )
		{
			// 当线程类实现Runnable接口时,
			// 如果想获取当前线程,只能用Thread.currentThread()方法。
			System.out.println(Thread.currentThread().getName()
				+ "  " + i);
		}
	}

	public static void main(String[] args)
	{
		for (int i = 0; i < 100;  i++)
		{
			System.out.println(Thread.currentThread().getName()
				+ "  " + i);
			if (i == 20)
			{
				SecondThread st = new SecondThread();     // ① 这里创建实现了Runable接口的类的实例
				// 通过new Thread(target , name)方法创建新线程
				new Thread(st , "新线程1").start();    //这里的意思是创建一个线程的target,也就是线程的目标
				new Thread(st , "新线程2").start();
			}
		}
	}
}

通过使用Callable和Future创建线程

import java.util.concurrent.*;
public class ThirdThread implements Callable<Integer>{
	public Integer call(){
		int i=0;
		for(;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"循环变量i的值"+i);
		}
		return i;
	}
	public static void main(String[] args){
		ThirdThread rt=new ThirdThread();
		FutureTask<Integer> task=new FutureTask<Integer>(rt);
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+
				" 循环变量i的值:"+i);
			if(i==20){
				new Thread(task,"有返回值的线程").start();
			}
		}
		try{
			System.out.println("子线程的返回值:"+task.get());   //这里居然有子线程的返回值,通过task来包装Callable类的rt实例,然后通过get方法来获取call()的返回值
		}
		catch(Exception ex){
			ex.printStackTrace();
		}
		
	}
}

线程

线程的生命周期

在线程的生命周期中,它要经过新建,就绪,运行,阻塞,死亡5种状态。

当创建一个新的线程

,也和其他的java对象一样,只是由java虚拟机为其分配内存,并且初始化其成员的变量。此时的线程对象并没有表现出任何的线程的动态特征,程序也不会执行线程的线程执行体。

当调用了线程的start()方法之后,

线程就处于就绪状态,java虚拟机为为其创建方法调用栈和程序计数器,处于这个状态的线程并没有开始运行,只是表示该线程可以运行。至于该线程何时开始运行,取决于JVM里线程调度器的调度;

不要去尝试调用线程对象中run()方法:

因为这样run()方法会立即执行,并且在返回之前无法并发执行,并且调用了之后线程就不再处于新建状态,不能再次执行start(),会引发异常
线程调度策略:抢占式调度策略

线程进入阻塞状态

sleep()
调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
线程视图获取一个同步监视器,但是该同步监视器,但是该同步监视器正在被其他线程所持有。
线程正在等待某个通知。
程序调用了现成的suspend()方法将线程挂起。但是这个方法容易导致死锁,所以应该尽量避免使用这个方法。

控制线程

join线程:如果想让当前执行的等待另一个线程完成,用join方法
后台线程:setDaemon()
线程睡眠:sleep()
线程让步: field()
设置线程优先级:setPriority()

线程同步安全问题:

举例:银行取钱问题,有一个账户,但是创建两个线程对账户进行取钱操作
账户类

public class Account
{
	private String accountNo;
	private double balance;
	public Account(){}
	public Account(String accountNo,double balance){
		this.accountNo=accountNo;
		this.balance=balance;
	}
	public void setAccount(String accountNo){
		this.accountNo=accountNo;
	}
	public String getAccount(){
		return this.accountNo;
	}
	public void setBalance(double balance){
		this.balance=balance;
	}
	public double getBalance(){
		return this.balance;
	}
	public int hashCode(){
		return accountNo.hashCode();
	}
	public boolean equals(Object obj){
		if(obj==this){
			return true;
		}
		if(obj!=null&&obj.getClass()==this.getClass()){
			Account at=(Account)obj;
			return at.getAccount().equals(this.accountNo);
		}
		return false;
	}
	
}

取钱线程:
public class DrawThread extends Thread
{
private Account account;
private double drawAccount;
public DrawThread(String name,double drawAccount,Account account){
super(name);
this.drawAccount=drawAccount;
this.account=account;
}

//线程的运行体,取出要取的钱
public void run(){
	//使用account来作为同步监视器,任何线程进入下面的代码块之前
	//必须先获得对account的锁定,-其他线程无法获得锁,也就无法修改它
	synchronized(account){   
		if(drawAccount<=account.getBalance()){
			System.out.println("取钱成功,吐出钞票:"+drawAccount+"元");
			try{
				Thread.sleep(1);
			}
			catch(InterruptedException ex){
				ex.printStackTrace();
			}
			account.setBalance(account.getBalance()-drawAccount);
			System.out.println("\t余额为:"+account.getBalance());
		}
		else{
			System.out.println("取钱失败");
		}
	}
	//同步代码块消失,该线程释放同步锁
}

}

取钱动作:
public class Draw
{
public static void main(String[] args){
Account at=new Account(“陈阔”,1000);
new DrawThread(“第一个取钱线程”,800,at).start();
new DrawThread(“第二个取钱线程”,800,at).start();

}

}

使用synchronized同步监视器来解决

synchronized(obj){
同步监视代码块
}

使用同步方法

public class Account2
{
	private String accountNo;
	private double balance;
	public Account2(){}
	public Account2(String accountNo,double balance){
		this.accountNo=accountNo;
		this.balance=balance;
	}
	public void setAccount(String accountNo){
		this.accountNo=accountNo;
	}
	public String getAccount(){
		return this.accountNo;
	}
	public double getBalance(){
		return this.balance;
	}
	//使用同步修饰符对方法进行修饰,同步方法的同步监视器是this,也就是调用这个方法的对象
	//因此对于同一个Account账户而言,任意时刻只能有一个线程获得对Account的对象的锁定,然后进入draw()方法执行取钱操作
	public synchronized void draw(double drawAmount){   
		if(drawAmount<=balance){
			System.out.println("取钱成功"+drawAmount+"元");
			
			//问题:为什么要在这里进行异常判断
			try{
				Thread.sleep(1);
			}
			catch(InterruptedException ex){
				ex.printStackTrace();
			}

			this.balance=this.getBalance()-drawAmount;
			System.out.println("\t余额为:"+this.getBalance());
		}
		else{
			System.out.println("取钱失败");
		}
	}
	public int hashCode(){
		return accountNo.hashCode();
	}
	public boolean equals(Object obj){
		if(obj==this){
			return true;
		}
		if(obj!=null&&obj.getClass()==this.getClass()){
			Account at=(Account)obj;
			return at.getAccount().equals(this.accountNo);
		}
		return false;
	}
	
}

线程的运行体代码

public class DrawThread2 extends Thread
{
	private Account2 account;
	private double drawAccount;
	public DrawThread2(String name,double drawAccount,Account2 account){
		super(name);
		this.drawAccount=drawAccount;
		this.account=account;
	}

	//线程的运行体,取出要取的钱
	public void run(){
		//这个方法在account自己的类中是使用了synchronized修饰符的,运行时就会对运行和这个方法的对象进行锁定,也就是account
		account.draw(drawAccount);  
		
	}

}

提示:在account里定义的draw()方法,不在run()中实现取钱逻辑,这种做法更加符合面向对象的规则,在面向对象中有一种就行的设计方法

Domain Driven Design(领域驱动设计):

认为每个类都应该是完备的领域对象,刚刚设计的类属于线程安全的类(该类的对象可以被多个线程安全地访问,每个线程调用该对象的任意方法都将得到正确的结果)
注意:可变类的线程安全是以降低程序的运行效率来作为代价的,为了减少线程安全所带来的负面影响,采用如下策略:
不要对线程安全的类的所有方法都进行同步,只对那些会改变竞争资源的方法进行同步。
如果可变类有两种运行环境:单线程环境和多线程环境,应该为该可变类提供两种版本。在单线程中保证性能,多线程安全中使用线程安全的版本

线程通信:

使用Object类提供的wait(), notify(), notifyAll() 三个方法
wait():导致当前线程等待,知道其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程.调用wait()方法的当前线程会释放对该同步监视器的锁定。
该方法有三种重载方式:无参(一直等待,知道其他线程通知);带毫秒参数的wait; 带毫秒微毫秒参数的wait();.
notify():唤醒此同步监视器上等待的某个单线程。

银行多个一个取款者和多个存款者

import java.util.concurrent.locks.*;
public class Account4
{
	private String accountNo;
	private double balance;
	//设置标志位判断是否有存款
	private boolean flag=false;

	public Account4(){}
	public Account4(String accountNo,double balance){
		this.accountNo=accountNo;
		this.balance=balance;
	}
	public void setAccount(String accountNo){
		this.accountNo=accountNo;
	}
	public String getAccount(){
		return this.accountNo;
	}
	public double getBalance(){
		return this.balance;
	}
	//使用同步修饰符对方法进行修饰,同步方法的同步监视器是this,也就是调用这个方法的对象
	//因此对于同一个Account账户而言,任意时刻只能有一个线程获得对Account的对象的锁定,然后进入draw()方法执行取钱操作
	//存钱函数
	public  synchronized void draw(double drawAmount){   
		try{
			if(!flag){
				wait();
			}
			else{	
				System.out.println(Thread.currentThread().getName()+"取钱成功"+drawAmount+"元");

				this.balance=this.getBalance()-drawAmount;
				System.out.println("\t余额为:"+balance);
				flag=false;
				notifyAll();
			}
		}
		catch(InterruptedException ex){
			ex.printStackTrace();
		}
		
	}
	//取钱函数
	public synchronized void deposit(double depositAmount){
		try{
			if(!flag){
				System.out.println(Thread.currentThread().getName()+"存钱成功"+depositAmount);
				balance+=depositAmount;
				System.out.println("\t余额为"+balance+"元");
				flag=true;
				notifyAll();
			}
			else{
				wait();
			}
		}
		catch(InterruptedException ex){
			ex.printStackTrace();
		}
	} 
	public int hashCode(){
		return accountNo.hashCode();
	}
	public boolean equals(Object obj){
		if(obj==this){
			return true;
		}
		if(obj!=null&&obj.getClass()==this.getClass()){
			Account at=(Account)obj;
			return at.getAccount().equals(this.accountNo);
		}
		return false;
	}
	
}
public class DepositThread extends Thread{
	private Account4 at;
	private double depositAmount;
	public DepositThread(String name,double depositAmount,Account4 at){
		super(name);
		this.at=at;
		this.depositAmount=depositAmount;
	}
	public void run(){
		for (int i=0;i<100 ;i++ )
		{
			at.deposit(depositAmount);
		}
	}
}
public class DrawThread2 extends Thread
{
	private Account4 account;
	private double drawAccount;
	public DrawThread2(String name,double drawAccount,Account4 account){
		super(name);
		this.drawAccount=drawAccount;
		this.account=account;
	}

	//线程的运行体,取出要取的钱
	public void run(){
		//这个方法在account自己的类中是使用了synchronized修饰符的,运行时就会对运行和这个方法的对象进行锁定,也就是account
		for (int i=0;i<100;i++ )
		{
			account.draw(drawAccount);  
		}
	}

}

public class Draw
{
	public static void main(String[] args){
		/*Account at=new Account("陈阔",1000);
		new DrawThread("第一个取钱线程",800,at).start();
		new DrawThread("第二个取钱线程",800,at).start();
		*/
		/*
		Account2 at2=new Account2("小明",1000);
		new DrawThread2("取钱线程第二种第一个",800,at2).start();
		new DrawThread2("取钱线程第二种第2个",800,at2).start();
		*/
		/*Account3 at3=new Account3("小红",1000);
		new DrawThread2("使用同步锁的取钱线程",800,at3).start();
		new DrawThread2("使用同步锁的取钱线程",800,at3).start();
		*/
		Account4 at4=new Account4("小刚",1000);
		new DrawThread2("取钱者",1000,at4).start();
		new DepositThread("存钱者甲",1000,at4).start();
		new DepositThread("存钱者乙",1000,at4).start();
		new DepositThread("存钱者丙",1000,at4).start()
	}
}

线程阻塞和死锁:如图所示的阻塞并不是死锁,对于这种情况,取钱者线程已经执行结束,而存款者线程只是等待其他取款者线程来取钱而已,而不是等待其他线程释放同步监视器。

一个问题(为什么只有一个存款者和一个取款者有时候还是会阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值