学习多线程之前,我觉得很有必要去学习下
[笔记][思维导图]读深入理解JAVA内存模型整理的思维导图
基础知识
- 锁除了让临界区互斥执行外,
还可以让释放锁的线程向获取同一个锁的线程发送消息 - 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。
从而使得监视器保护的临界区代码必须要从主内存中去读取共享变量。 - 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。
- 加锁使用的是同一个锁的话,使用syncronized的静态方法和普通方法需要注意的是:静态方法:同时只能被一个线程访问,但是可以访问其他被syncronized修饰过的普通方法,所以:如果存在静态和非静态方法中都修改了同一个数据,那么就会出现并发问题
针对基础知识中的第4点验证
其他三点,请看java内存模型
例子:
public class SyncStatic {
public static void main(String[] args) throws InterruptedException {
public class SyncStatic {
public static void main(String[] args) throws InterruptedException {
final A a = new A();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
a.addStatic();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
a.sub();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t2.start();
t1.join();
t2.join();
System.out.println(a.num);
}
}
class A{
public static int num = 0;
public static synchronized void addStatic(){
++num;
System.out.println("addStatic:" + num);
}
/**
* 想看到正确的结果,请把这里的static注释放开
*/
public /*static*/ synchronized void sub(){
--num;
System.out.println("sub:" + num);
}
}
某一次运行结果:
addStatic:0
sub:0
sub:0
addStatic:1
addStatic:1
sub:0
sub:-1
addStatic:0
sub:-1
addStatic:0
addStatic:1
sub:1
addStatic:1
sub:1
sub:0
addStatic:1
sub:1
addStatic:1
addStatic:2
sub:2
2
说明:
一个A类。两个线程:一个线程相加10次,一个线程相减10次,最后的结果肯定是不变的。但是这里实现正面变化了。所以说:要注意被syncronized修饰过的静态方法
充钱取钱的列子
**
* Created by zhuqiang on 2015/8/5 0005.
* 模拟账户
*/
public class Account {
private String userName; //用户
private double money; //余额
public synchronized void add(double money){
this.money = this.money + money;
System.out.println(this.getUserName() + ",+100:余额:" + this.getMoney());
}
public synchronized void subtract(double money){
this.money = this.money - money;
System.out.println(this.getUserName() + ",-100:余额:" + this.getMoney());
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
/**
* Created by zhuqiang on 2015/8/5 0005.
* atm 机器,往里面不断的充值
*/
public class AtmAdd implements Runnable {
private Account account;
public AtmAdd(Account account){
this.account = account;
}
@Override
public void run() {
for (int i = 1;i <=100;i++){
account.add(100);
}
}
}
/**
* Created by zhuqiang on 2015/8/5 0005.
* atm 机器,往里面不断的扣钱
*/
public class AtmSub implements Runnable {
private Account account;
public AtmSub(Account account){
this.account = account;
}
@Override
public void run() {
for (int i = 1;i <=100;i++){
account.subtract(100);
}
}
}
/**
* Created by zhuqiang on 2015/8/5 0005.
*/
public class Client {
public static void main(String[] args) throws Exception {
Account account = new Account();
account.setMoney(0);
account.setUserName("小明");
Thread add = new Thread(new AtmAdd(account));
add.start();
Thread sub = new Thread(new AtmSub(account));
sub.start();
add.join(); //等待该线程终止,主线程才执行
sub.join();
System.out.println(account.getMoney());
}
}
说明:
运行结果就不贴出了;
该示例有2个线程,一个线程不断的往账户中冲钱,一个线程不断的往账户中扣钱。正确同步的结果是最后账户中的钱是不变的。因为执行的次数都一样
使用非依赖属性实现同步
一般在方法上加syncronized,是使用当前对象的锁,比如
syncronized(this){
...
}
那么可以使用不同的对象来替代this,并发操作不同的共享数据。而不用全部都等待this锁。
比如:下面这种情况,a,b之间没有什么关系。但是又要不相关的线程也要排队等待。就可以使用不同的对象来替代this
int a ; int b
syncronized(this){
b++
...
}
syncronized(this){
a++
...
}