折纸的不归路(21)

线程后续

线程安全 synchronized

1.修饰代码块

synchronized(对象){
     
	//对于被锁住的对象的操作
}

对象:共享区
一个对象只能拥有一把对象锁
代码块里的内容:临界区

互斥线程
并发访问的线程上同步锁之后就称为线程互斥
谁抢到了对象锁的线程就先执行
没有抢到对象锁的线程就等待对象锁
加同步锁,夹在共享对象上
每一个对象都有唯一的一把锁

2.修饰方法:同步方法

一般做为修饰符放在返回值后面
示例:

public synchronized void getMoney(){
     }

此时被锁住的共享区的对象是this
一般同步方法用在本类的声名中
一般来讲:
同一种功能实现会提供两种版本,一种是单线程下效率较高的线程不安全版本
另一种是多线程情境下效率较低单线程安全的版本
StringBuffer
StringBuilder

ArrayList
Vector

线程通信:线程同步

也是操纵同一对象,可以控制线程的执行顺序
wait(),让线程等待,等待之后进入阻塞状态(等待池),需要别的线程唤醒
notify(),唤醒等待池中任意一条线程
notifyAll(),唤醒等待池中所有等待的线程

例:生产者:用来计算1+100的和。和就是产品
消费者:能够得到你这个和输出一下就可以了
生产者先执行,消费者后执行。 结果5050
消费者先执行,生产者后执行。 结果0
生产者和消费者两个是不同的逻辑,所以需要两个线程类
Sender 生产者
Printer 消费者

public class ResultTest {
     
	public static void main(String[] args) {
     
		//新建一个共同操作的对象result
		Result r = new Result();//旗标位为false
		//产生两个线程实例
		Sender s = new Sender(r, "生产者");
		Printer p = new Printer(r,"消费者");
		//想要的结果,不管哪个先进入就绪状态,都是生产者先执行
		s.start();
		p.start();
		
	}
}

// 生产者的线程类
class Sender extends Thread {
     
	private Result result;

	public Sender(Result result, String name) {
     
		super(name);
		this.result = result;
	}

	@Override
	public void run() {
     
		// 从0+到100
		int sum = 0;
		for (int i = 0; i <= 100; i++) {
     
			sum += i;
		}
		
		
		if(result.isFlag() == false) {
     
			//如果当前没有等待的线程就睡眠0.2秒
			//生产者线程阻塞,让出CPU的使用权,消费者跟着执行
			try {
     
				sleep(200);
			} catch (InterruptedException e) {
     
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		result.setValue(sum);
		synchronized(result) {
     
			result.notifyAll();
		}
	}
}

// 消费者的线程类
class Printer extends Thread {
     
	private Result result;

	public Printer(Result result,String name) {
     
		super(name);
		this.result = result;
	}
	@Override
	public void run() {
     
		synchronized(result) {
     
			try {
     
				result.setFlag(true);
				result.wait();
			} catch (InterruptedException e) {
     
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	System.out.println("结果"+result.getValue());
	}
}

旗标的代码如下

/**
 * 模拟一个结果数据
 * 
 * @author Acer
 *
 */
public class Result {
     
	private int value;
	//旗标,也叫做标志位
	private boolean flag;

	public boolean isFlag() {
     
		return flag;
	}

	public void setFlag(boolean flag) {
     
		this.flag = flag;
	}

	public int getValue() {
     
		return value;
	}

	public void setValue(int value) {
     
		this.value = value;
	}
}

例2:存钱取钱
账户里面有钱才可以取,账户里面存进去钱,才可以取出钱
假设:男朋友的线程是用来存钱的,女朋友的线程是用来花钱的

public class Account {
     

	// 属性
	// 账号
	private String accountNo;
	// 余额
	private double balance;

	// 设置一个标志位来判断当前账户里有没有钱
	private boolean flag;// 默认值为false,代表当前账户中没有钱

	public Account() {
     
	}

	public Account(String accountNo, double balance) {
     
		super();
		this.accountNo = accountNo;
		this.balance = balance;
	}

	// 提供一个取钱的方法
	public synchronized void getMoney(double money) throws InterruptedException {
     
		// 判断
		if (flag == false) {
     
			wait();
		} else {
     
			// 判断一下输入的钱是否大于余额
			if (money <= getBalance() && money >= 0) {
     
				System.out.println(Thread.currentThread().getName() + "本次取了" + money+"元");
				// 余额的计算
				setBalance(getBalance() - money);
				try {
     
					Thread.sleep(100);// 抱着锁睡的,不会释放对象锁
				} catch (InterruptedException e) {
     
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("余额" + getBalance());
				flag = false;
				notifyAll();// 取完钱之后唤醒别的等待的线程
			} else {
     
				System.out.println("你不知道自己多少钱么");
			}
		}
	}

	public synchronized void saveMoney(double money) throws InterruptedException {
     
		if (flag == true) {
     
			wait();
		} else {
     
			System.out.println(Thread.currentThread().getName()+"本次存了" + money + "元");
			setBalance(getBalance() + money);
			System.out.println("余额" + getBalance());
			flag = true;
			notifyAll();
		}
	}

	public String getaNumber() {
     
		return accountNo;
	}

	public void setaNumber(String accountNo) {
     
		this.accountNo = accountNo;
	}

	public double getBalance() {
     
		return balance;
	}

	public void setBalance(double balance) {
     
		this.balance = balance;
	}

	@Override
	public String toString() {
     
		return "Account [accountNo=" + accountNo + ", balance=" + balance + "]";
	}

	@Override
	public int hashCode() {
     
		final int prime = 31;
		int result = 1;
		result = prime * result + ((accountNo == null) ? 0 : accountNo.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
     
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Account other = (Account) obj;
		if (accountNo == null) {
     
			if (other.accountNo != null)
				return false;
		} else if (!accountNo.equals(other.accountNo))
			return false;
		return true;
	}

}

/**
 * 男朋友存钱流程
 * 
 * @author Acer
 *
 */
public class BoyThread extends Thread {
     
	// 所操纵的数据?
	private Account account;// 同一个账户
	// 传递取的钱
	private double money;

	public BoyThread(Account a, double money, String name) {
     
		super(name);
		this.money = money;
		account = a;
	}

	@Override
	public void run() {
     
		// 调用存钱的方法,存100次
		for (int i = 1; i <= 100 ; i++) {
     
			try {
     
				account.saveMoney(money);
			} catch (InterruptedException e) {
     
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

/**
 * 女朋友管卡线程类
 * @author Acer
 *
 */
public class GirlThread extends Thread {
     
	// 所操作的数据?
	private Account account;// 同一个账户
	// 传递取的钱
	private double money;

	public GirlThread(Account a, double money, String name) {
     
		super(name);
		this.money = money;
		account = a;
	}

	@Override
	public void run() {
     
		// 调用取钱的方法,连续取100次
		for (int i = 0; i < 100; i++) {
     
			try {
     
				account.getMoney(money);
			} catch (InterruptedException e) {
     
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

/**
 * 各自执行线程
 * @author Acer
 *
 */
public class CardTest {
     
	public static void main(String[] args) {
     
		Account a = new Account("7355608",0);
		//声明两个线程
		BoyThread bt = new BoyThread(a,800,"boy");
		GirlThread gt = new GirlThread(a, 800, "girl");
		
		bt.start();
		gt.start();
	}
}

死锁

如何发生的:两个线程都拥有对方的对象锁不能释放
如何解决:对象锁的获取顺序保持一致,完成一个较好的资源调度即可

相关代码:

package day19;

public class DeadThreadTest {
     
	public static void main(String[] args) {
     
		DeadThreadTest dt = new DeadThreadTest();
		Dead1 d1 = new Dead1(dt);
		Dead1 d2 = new Dead1(dt);
		d1.start();
		d2.start();
	}

	static class R {
     
		int value = 0;
	}

	private R r1 = new R();
	private R r2 = new R();

	public void write() throws InterruptedException {
     
		synchronized(r1) {
     
			Thread.sleep(200);
			synchronized(r2) {
     
				System.out.println("write");
			}
		}
	}
	public void read() throws InterruptedException {
     
		synchronized(r2) {
     
			Thread.sleep(200);
			synchronized(r1) {
     
				System.out.println("read");
			}
		}
	}
}

class Dead1 extends Thread {
     
	private DeadThreadTest dt;
	public Dead1(DeadThreadTest dt) {
     
		this.dt=dt;
	}
	@Override
	public void run() {
     
		try {
     
			dt.write();
		} catch (InterruptedException e) {
     
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class Dead2 extends Thread {
     
	private DeadThreadTest dt;
	public Dead2(DeadThreadTest dt) {
     
		this.dt = dt;
	}
	@Override
	public void run() {
     
		try {
     
			dt.read();
		} catch (InterruptedException e) {
     
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

锁对象Lock

jdk提供了很多锁对象供我们去使用,如读写锁
在jdk中后缀名为lock的大部分类
使用步骤:
声明一个锁对象,在需要的地方进行lock
最后在逻辑完成之后,记得释放锁对象lock
代码实例:

private final ReentrantLock lock = new...
	try{
     
		lock.lock();
		//写要被锁住的对象的执行,临界区的代码
	}finally{
     
		lock.unlock();
	}

线程池

池化编程:预先创建好一定数量的线程,避免多次新建线程

interrupt打断

打断正在睡眠的线程,如果打断正在执行的线程会报错
isInterrupted:判断当前线程是否被打断
interrupted:清空打断信息
可以调用Thread类中的非静态方法isInterrupted来返回当前线程的interrupted status的状态值。这个值只有两种情况:true和false

true 表示当前有 线程要打断我们的阻塞状态
false表示当前没有线程要打断我们的阻塞状态

二.心得

今天继续学习的线程的相关内容,今天就没有昨天那么容易了,感觉还是比较绕的,上课代码也没有跟住敲,只能下来再慢慢研究,在取钱的那个相关代码还碰见了一个问题,设置取钱100次,但是程序循环只执行了50次就停下了,也没有结束,研究半天也没研究出来结果,只能等后续的再研究了.

你可能感兴趣的:(java)