窗外狂风大作,实在不想在睡觉时刻本着不完成誓不罢休不睡觉的精神在这邹文章。
的确,哪怕再小的知识点要吃透,不花点功夫可是没可能的。所以就线程机制给我个把星期估计也讲不明白,到头来只是重复老师的话或者网上技术牛人的博客。此刻呢,也只能绕过代码层面,说说理解和
体会吧。
首先,我习惯把知识整理成小体系,逻辑清晰,而每一块的具体当然有大把时间上网搜,进而学习。
不闲扯~
- 概念,与进程联系与区别。
- 用方法。Thread和Runnable
- 线程游戏,熟悉线程方法使用,出错。
- 进一步了解线程原理,工作机制,提出同步,异步概念。
接下来就同步详细介绍。异步相关概念将在后续学习中逐步介绍。
首先,线程同步到底是什么概念?
同步是表示线程之间存在的一种状态,还是存在的一种问题,还是解决线程共享资源被争夺的一种解决方法?
因为“同步”二字本身容易混淆,“同”非共同,一起,而是“协作”,“配合”的意思,即“排队”,一个一个对共享资源进行操作。有这么一句话:线程同步就是线程排队,同步就是避免线程“同步“执行。
综述,线程同步首先是一种解决问题的方法,即线程同步机制(动作)。也是问题被解决之后的一种状态描述,即线程之间同步了(状态)。当然,把它理解成问题的思路肯定是把“同“ 想成 ”共同“的意思。
这样,理解线程同步相关概念,例如,同步锁(synchronized关键字),wait()与notify()方法,以及生产消费者模型,双缓冲,线程池等之间的联系。
其次,何时需要线程同步以及为什么需要?
A.对共享资源的使用,引起争夺与混乱。
B.共享资源是变量,即任何线程可以改其属性
举个简单的例子,线程A和线程B分别对变量x(=0)进行加1和减1的操作。而线程的执行顺序是未知的,且该操作不是原子性的,即在计算机底层连简单的+1操作也是分为3个步骤的(从内存取出,+1,放回内存),操作系统本着充分利用资源和时间响应的精神,自然会在各种调度程序的控制下,在A线程未完成加1操作之前,B线程进行减1的操作。而最终x的值无法预知。
我们当然不能让这种事情让程序出错啊~那就让线程同步吧~即让线程协同配合着使用共享资源。
有这么几种方法: 1.同步锁 2.等待/通知模式
1.同步锁的基本思路是指:
对共享资源加锁,且只有一把钥匙。同一时刻只存在一个持有钥匙的线程在使用它。
(情景:给银行账户存取款。银行初始有100元,现在启动6个线程,随即进行存取操作,显而易见,必须要线程同步.)
A.synchronized关键字——给方法或者代码段加锁
a)锁定方法
Bank bank=new Bank();
myThread thread1=new myThread(bank,20);
myThread thread2=new myThread(bank,-120);
myThread thread3=new myThread(bank,50);
myThread thread4=new myThread(bank,90);
myThread thread5=new myThread(bank,-40);
myThread thread6=new myThread(bank,200);
thread1.start();
.......
public class myThread extends Thread {
private int money;
private Bank bank;
myThread(Bank bank, int money) {
this.bank = bank;
this.money = money;
}
public void run() {
bank.process(money);
}
}
public class Bank {
private int total = 0;
public Bank(int total) {
this.total = total;
}
public void process(int money) {
try {
Thread.sleep(100);
this.total += money;
System.out.println("对银行已操作:" +
money + "银行现存:" + this.total);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行上述代码,可发现完全是混乱的,而在process()方法前加synchronized关键字即可锁定该方法,即每次只让一个线程改变账户存款。
b)锁定方法块,即不是对整个方法进行锁定,只对那些操作共享资源的关键代码段进行加锁。
synchronized (this){
this.total+=money;
System.out.println("对银行已操作:"+money+"银行
现存:"+bank.getTotal());
}
注意:synchronized ()括号里需要一个对象,此例中,可以直接填this,或者new一个Object对象或者一个大小为0的数组,即:
Object obj=new Object();
synchronized (obj){};
B. 用Lock加锁。
首先实例化一个Lock对象,在process()方法中传进去,或者利用单例模式保证每个线程拥
有的是同一把锁即可。如:
void process(int money,Lock lock) {
try {
lock.lock();
Thread.sleep(100);
this.total += money;
System.out.println("对银行已操作:" +
money + "银行现存:" + this.total);
Thread.sleep(100);
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.等待/通知机制——wait()/notify()方法
关于wait/notify,还有生产消费者模型与线程同步的关系,区别与应用将在后续继续呈上自己的心得哦~~