线程同步:即一个项目工程在执行的过程中,可能存在多个线程同时对一个对象进行操作,结果操作对象的数据不同步的问题,从而使得项目工程无法按照正常的程序运行下去。这里主要介绍两种常见模型的同步问题。
一:银行取现问题模型
对于一个银行账户,如果同时在ATM和柜台两处进行取钱,就有可能使得账户数据不同步的问题
这是取现过程中内部判断问题时代码:
package com.lol;
public class Account {
private int count;
public Account(int count){
this.count=count;
}
public <span style="color:#cc0000;"><strong>(synchronized)</strong></span> int getCash(int cash) {
if(count<cash){
return -1;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count=count-cash;
return count;
}
}
这是具体实现取现的步骤:
package com.lol;
public class GetCashThread extends Thread{
private Account account;
private String operation;
private int cash;
public GetCashThread(Account account, String operation, int cash) {
this.account=account;
this.operation=operation;
this.cash=cash;
}
public void run() {
int count=account.getCash(cash);
if(count==-1){
System.out.println("本人在"+operation+"提现失败,取现额度是:"+cash);
}else{
System.out.println("本人在"+operation+"提现成功,取现额度是:"+cash+",卡中余额:"+count);
}
}
}
然后数据的给定:package com.lol;
public class Test {
public static void main(String[] args) {
Cash();
}
private static void Cash() {
Account account=new Account(5000);
GetCashThread cashThread1=new GetCashThread(account,"ATM",3000);
GetCashThread cashThread2=new GetCashThread(account,"柜台",3000);
cashThread1.start();
cashThread2.start();
}
}
就这样将银行取现的模型写出来,但是在这个执行过程中,由于中途可能出现时间同时进行的问题,导致结果违背了实际的操作:
违背了实际情况,也就是在卡中余额不足的情况下依然将现金提取了出来,这是因为这是由两个线程同时进行的,使得一个线程在判断的时候另一个线程并没有结束,也就是判断条件的时候都是的条件成立。
这种类型的同步问题解决:同步锁关键字(synchronized:锁定当前对象或者方法),也就是说synchronized是针对方法或者对象直接使用的,在上面的银行提现问题中也是一样的,如果我们针对方法进行改进,只需要在我们在判断条件也就是在取款扣钱的过程中添加一个关键字synchronized在int之前,如上红色字母即可。如果针对对象的话,这个对象必须是与两个线程同时进行有关的对象。
synchronized原理:在被锁定的方法或者对象中只能有一个程序进行,一家独用。
synchronized代码块中的语句只能有一个线程在执行
1)任意一个对象都有一个标志位,有1和0两种状态
2)当程序执行到synchronized代码块的时候线程会检查对象的标志位是1还是0
3)如果是1则执行程序,同是将对象的标志位设置为0,其他线程执行到synchronized代码块时一看对象标志位为0 则线程会阻塞,一直等到对象的标志位为1再执行下面的程序
二:生产消费模型
生产消费加上一个中转站组成的是一个完整的流程,在这里暂且将这个中转站定义成一个列表,用来存储商品,当生产者生产出商品,消费者才可以消费,而消费者消费商品后,生产者才可以生产商品。这样生产者就得时时关注着列表,及时生产,消费者也得时时关注着列表,才能产生消费。
这时,如果我们加一个通知,也就是wait/notify机制,在互相执行完自己操作时对上一个对象进行通知,使得另一个程序得以进行,需要注意的是:
1)wait和notify必须在同步锁之内使用 (2)同步锁锁定对象和wait、notify对象必须同一个 (3)当对象wait挂起状态时候是会释放同步锁的
wait/notify机制是当对象调用wait方法时,当前线程就会处于wait状态,直到收到另一个线程的通知,这样就避免了浪费问题,也可以避免因为线程之间运行是出现的堵塞造成的程序不能进行正常运行的问题,可以”腾出“更多的空间去处理其他程序。
如下面例子中:
package com.SKT;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<IPhone7> list = new ArrayList<IPhone7>();
//生产线程对象
ProductThread pt = new ProductThread(list);
CustomerThread ct = new CustomerThread(list);
pt.start();
ct.start();
}
}
生产线程:package com.SKT;
import java.util.ArrayList;
public class ProductThread extends Thread{
private ArrayList<IPhone7> list;
public ProductThread(ArrayList<IPhone7> list) {
this.list = list;
}
public void run() {
// 不停地执行生产操作
int count = 1;
while (true) {
synchronized (list) {
if (list.size() > 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产一个手机
IPhone7 iphone = new IPhone7("产品序号:" + count);
list.add(iphone);
System.out.println("生产商生产了一个手机,型号为:" + iphone.name);
count++;
list.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
消费线程:package com.SKT;
import java.util.ArrayList;
public class CustomerThread extends Thread{
private ArrayList<IPhone7> list;
public CustomerThread(ArrayList<IPhone7> list) {
this.list = list;
}
public void run() {
// 不停地执行生产操作
while (true) {
synchronized (list) {
if (list.size() ==0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产一个手机
IPhone7 iphone = list.remove(0);
System.out.println("消费者从仓库中取出了一个手机,型号为:"+iphone.name);
list.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
封装的对象类:
package com.SKT;
public class IPhone7 {
public String name;
public IPhone7(String name) {
this.name = name;
}
}
由此才得出的一条流畅并且不浪费的生产线(不需要消耗时间去时刻监督)得到的结果如下并且一直持续下去。当一个工程需要多个线程同时工作时,而线程的特点是同时进行,没有顺序而言,所以这相当于将每个线程又当作一个线程的各个步骤,这些线程又同时工作,所以才会有操作同一对象时产生的数据不同步的情况,这时可以将这几个同时操作同一对象的线程不能同时进行,从而解决问题。