java线程冲突与线程同步
/**
* 线程冲突和线程同步
* 当多个线程同时对同一个对象进行存取会出现线程冲突
* java中通过线程同步synchronized限制对象的并行访问,线程同步时当某个线程正在访问该对象,其他正要访问的线程进入阻塞状态,直至前一个线程执行完受同步影响的语句块,下一个线程再变为就绪状态
*/
public class Test8Thread implements Runnable{
public static class Account{
//创建账户类,存款不能为负数
String id;
int money;
public Account(String id, int money) {
this.id = id;
this.money = money;
}
}
Account targetAccount;
//要访问的账户
public Test8Thread(Account targetAccount) {
this.targetAccount = targetAccount;
}
@Override
public void run() {
if (targetAccount.money>=800){
System.out.println("正在取款,请稍等");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
targetAccount.money -= 800;
System.out.println("取款成功,剩余金额: "+targetAccount.money);
}else {
System.out.println("余额不足,取款失败");
}
}
public static void main(String[] args) {
Account a1 = new Account("101",1000);
new Thread(new Test8Thread(a1),"1号取款人").start();
new Thread(new Test8Thread(a1),"2号取款人").start();
/*结果为:
正在取款,请稍等
正在取款,请稍等
取款成功,剩余金额: 200
取款成功,剩余金额: 200
两个线程几乎同时读取到money=1000都判断>800,又几乎同时用1000-800赋值给money
如果其中一个线程先一步完成money-=800,另一个线程会计算200-800赋值给money产生负数
*/
}
}
class TakeMoney implements Runnable{
Test8Thread.Account target;
public TakeMoney(Test8Thread.Account target) {
this.target = target;
}
@Override
public void run() {
synchronized (target){
//synchronized(锁对象){同步代码} 在执行语句块的内容时给对象(必须是对象)上锁,同步代码执行时具有线程互斥的能力(并行变串行)
if (target.money>=800){
System.out.println(Thread.currentThread().getName()+"正在取款,请稍等");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
target.money-=800;
System.out.println(Thread.currentThread().getName()+"取款成功");
}else {
System.out.println(Thread.currentThread().getName()+"余额不足,取款失败");
}
}
System.out.println(Thread.currentThread().getName()+"结束,余额为:"+target.money);
//同步语句块外的内容仍然为并行
}
public static void reset(Test8Thread.Account t){
synchronized (t){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t.money=700;
}
}
public static void main(String[] args) {
Test8Thread.Account a1 = new Test8Thread.Account("101",1000);
Thread t1 = new Thread(new TakeMoney(a1),"1号取款人");
Thread t2 = new Thread(new TakeMoney(a1),"2号取款人");
t1.start();
t2.start();
/*结果为:
1号取款人正在取款,请稍等
1号取款人取款成功
1号取款人结束,余额为:200
2号取款人余额不足,取款失败
2号取款人结束,余额为:200
t1执行synchronized语句时t2阻塞,等t1执行完语句后t2进入就绪状态,判定money<800取款失败
*/
Test8Thread.Account tar = new Test8Thread.Account("102",1000);
Thread t3 = new Thread(new TakeMoney(tar));
t3.start();
reset(tar);
/*结果为
Thread-0余额不足,取款失败
Thread-0结束,余额为:700
synchronized锁对象为同一对象时,执行不同语句块也会线程互斥
*/
}
}
class CustomizeArray{
private int[] arr = new int[10];
public synchronized void initialize(){
//synchronized修饰方法时锁对象为this/当前对象
for (int i =0;i<10;i++){
arr[i]=i;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Arrays.toString(arr));
}
public synchronized void sum(){
synchronized (this){
//锁对象为this,并且语句块涵盖整个方法体可改写为方法的修饰词
int sum=0;
for (int i:arr){
sum+=i;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("和为:"+sum);
}
}
}
class InitialThread implements Runnable{
//执行arr初始化
CustomizeArray arr;
public InitialThread(CustomizeArray arr) {
this.arr = arr;
}
@Override
public void run() {
arr.initialize();
}
}
class SumThread implements Runnable{
//执行arr求和
CustomizeArray arr;
public SumThread(CustomizeArray arr) {
this.arr = arr;
}
@Override
public void run() {
arr.sum();
}
}
class Test9Thread{
public static void main(String[] args) {
CustomizeArray arr = new CustomizeArray();
Thread t1 = new Thread(new InitialThread(arr));
Thread t2 = new Thread(new SumThread(arr));
t1.start();
t2.start();
//结果为
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//和为:45
//多个线程访问同一个锁对象,任何一个线程执行任何一段同步代码时,都会线程互斥,其他线程进入阻塞状态等待唤醒
}
}