一、甲乙两人每次一千,分三次分别向同一个账号各自存入三千:
思路:①提取共同数据②编写用于创建多线程对象的继承类③创建测试类,通过线程对象.start()方法创建线程
其中将账号单独提取出来构建一个类,可以有效的减少账号与人之间的耦合性。账号需要编写余额属性,一个带参的构造方法(需要通过在测试类中创建账号类型的引用类型数据对象,将账号对象通过引用类型方式作为人构造器中的形参传入,从而将账号与人联系起来),以及一个存钱的方法。
//构建共享数据
class Account{
private double balance;
public Account(int balance) {
this.balance = balance;
}
//对象中需要有存钱方法
public synchronized void SaveMonney(double monney){
if (monney > 0){
balance += monney;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
}
}
}
//构建对象创造类Customer,便于构造甲乙两个对象
class Customer extends Thread{
private Account acc;
public Customer(Account acc){
this.acc = acc;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
acc.SaveMonney(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(0);
Customer customer1 = new Customer(account);
Customer customer2 = new Customer(account);
customer1.setName("甲");
customer2.setName("乙");
customer1.start(); //调用run()方法
customer2.start();
}
}
注意:此处的同步监视器为this,因为synchronized同步方法在Account类里面,且测试类中只new了一个Account对象,所以此时Account对象为唯一的对象,即可用this。
二、使用两个线程打印1-100,两个线程交替打印:
思路:①创建一个Number类实现Runnable接口②创建测试类,通过线程对象.start()方法创建线程。此问题中使用了notify()与wait()两个方法,分别在处理共享数据代码前与代码后使用,notify()表示重新加锁,wait()表示释放锁。于是得到的流程为,第一个线程进去完成共享数据操作,到达wait()方法后,释放同步监视器,完成它操作的同时,第二个线程由于锁开了于是进来了,首先遇到notify(),马上加锁限制此时只能一个线程进入,完成共享数据操作后,到达wait()方法,释放锁。如此循环,最后得到两个线程交替有序的运行操作。
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
notify();
if (number <=100){
System.out.println(Thread.currentThread().getName() + "打印的数字为:" + number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else
break;
}
}
}
}
public class NumberTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
注意,wait()与notify()只能在同步代码块与同步方法中使用,不能在lock中使用。wait()与notify()都必须是由同一个同步监视器调用,否则会出现java.lang.IllegalMonitorStateException错误,且wait()与notify()都是定义在Object类中的方法,所以说,若不再synchronized括号内,则调用者会由this变为Object。
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true) {
notify(); //此时调用notify()方法的不是this同步监视器,而是Object类,会报错。
synchronized (this) {
if (number <=100){
System.out.println(Thread.currentThread().getName() + "打印的数字为:" + number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else
break;
}
}
}
}
三、生产者有固定的生产上限,比如二十个,达到二十个则会被叫停,若产品少于二十则会通知生产者继续生产;消费者可以取走产品,若店里没有产品了,则被通知等一下,如果有产品了则通知消费者取走产品。
思路:整体思路的创建与上述相同,可以培养一下从整体框架的构建到局部细节的编写。例如先把共享数据类,继承Thread类的两个类,以及测试类搭建好。通过测试类知道我要干什么,需要什么对象,创建对象是否需要带参构造器,创建好对象后,需要调用什么方法。此时需要的细节方法操作就都一目了然,通过art+enter可以在对应的类中创建构造器与方法。
//测试类
public class ProductTest {
public static void main(String[] args) {
Produce produce = new Produce(0);
Productor p1 = new Productor(produce);
Customer c1 = new Customer(produce);
//修改线程名字
p1.setName("生产者1");
c1.setName("消费者1");
//开始线程操作
p1.start();
c1.start();
}
}
//共享数据
class Produce{
private int num =0;
public Produce(int num){
this.num = num;
}
//生产产品
public synchronized void producer(){ //此时同步监视器为this,即为Produce创建的对象produce。因为从始至终就创建了一个produce对象
if (num < 20){
num++;
notify();
System.out.println(Thread.currentThread().getName()+"正在生产第"+num+"件产品");
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consume(){ //此时同步监视器为this,即为Produce创建的对象produce。因为从始至终就创建了一个produce对象
if (num > 0){
notify();
System.out.println(Thread.currentThread().getName()+"正在消费第"+num+"件产品");
num--;
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//线程创建类Productor
class Productor extends Thread{
private Produce produce;
public Productor(Produce produce){
this.produce = produce;
}
@Override
public void run() {
System.out.println(getName()+"开始生产>>>>>>>>>>>>>>>>>>>>>>>");
while (true){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
produce.producer();
}
}
}
//线程创建类Customer
class Customer extends Thread{
private Produce produce;
public Customer(Produce produce){
this.produce = produce;
}
@Override
public void run() {
System.out.println(getName()+"开始消费>>>>>>>>>>>>>>>>>>>>>>>");
while (true){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
produce.consume();
}
}
}
注意:notify()需要考虑放置的位置,因为producer()与consume()都是用的同一个同步监视器,同一时间只能进入一个synchronized方法中,所以若当进入到producer()方法创建产品时,随之consume()会陷入阻塞状态(两个方法随机进入,producer()也可能进入阻塞状态,这里举例consume()进入阻塞状态),当完成此方法后,调用wait()释放锁。此时要考虑notify()放置的位置,只要producer()方法中num++一次,消费者就可进行消费产品操作,所以考虑将notify()放到num++后。