1 线程同步
线程的同步是指协同、协助、互相配合,指多个线程协同配合访问临界资源。
为什么要进行线程的同步?
主要是线程可见性问题
java 中如何实现线程同步?
一般说来java线程实现同步有以下几种方式
1通过synchronized实现同步
这里主要有两种方式,分别是同步块,与同步方法:
同步块:
public void saveMoney2(int money){
synchronized (this){
account += money;
}
}
其中这里的this指的是当前调用该方法的对象,这样锁就是当前对象的锁,也可以换成其他的对象,这样就表示锁是其他对象的锁。
同步方法:
public synchronized void saveMoney(int money){
account += money;
System.out.println(Thread.currentThread().getName()+" 余额:<<----- " + getAccount());
}
同步方法中锁表示当前对象的锁,即调用该方法的锁。
下面看一个完整的线程同步的例子
public class Bank {
private int account;
public int getAccount() {
return account;
}
// public void saveMoney(int money){
// account += money;
// }
public synchronized void saveMoney(int money){
account += money;
}
public void saveMoney2(int money){
synchronized (this){
account += money;
}
}
// public void getMoney(int money){
// account -= money;
// }
public synchronized void getMoney(int money){
account -= money;
}
public class SaveTask implements Runnable {
private Bank bank;
public SaveTask(Bank bank){
this.bank = bank;
}
@Override
public void run() {
while (true){
bank.saveMoney(20);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* Created by qiyei2015 on 2017/2/11.
*/
public class TakeTask implements Runnable {
private Bank bank;
public TakeTask(Bank bank){
this.bank = bank;
}
@Override
public void run() {
while (true){
bank.getMoney(30);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试代码:
private static void testSynchronzied(){
Bank bank = new Bank();
SaveTask saveTask = new SaveTask(bank);
TakeTask takeTask = new TakeTask(bank);
for (int i = 0 ;i < 10;i++){
new Thread(saveTask,"存钱 线程" + (i+1)).start();
new Thread(takeTask,"取钱 线程"+ (i+1)).start();
}
}
程序输出:
2 线程通信
这里的通信主要是指线程的唤醒,比如一个线程进入阻塞状态,另一个线程去唤醒它。用到的主要是wait()与notify()。notifyAll()
wait():Object类中方法,线程中调用了wait()方法,线程会进入阻塞等待状态,并释放同步锁,线程会进入等待池。因此需要在同步块或者同步方法中调用
notify():Object类中方法,唤醒同一锁中等待池中的一个线程,唤醒的具体线程由虚拟机实现
notifyAll();Object类中方法,唤醒同一锁中等待池中的所有方法。
接着上面的例子,我们发现我们的余额会变成负数,这个与实际情况肯定是不相符的。因此我们需要修改我们的程序,让余额不能为负数,这里就会用到我们的线程通信的知识了。
修改取钱代码,只有取了之后的余额大于200。才能取钱,否则让取钱线程在此处等待。
public synchronized void getMoney(int money){
//如果小于200,则等待存钱线程
if ((account - money) < 200){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
account -= money;
System.out.println(Thread.currentThread().getName()+" 余额:------>> " + getAccount());
}
}
修改存钱线程,当发现余额大于200时,就表明可以取钱了。这样我们就能保持我们的余额永远大于200。
public synchronized void saveMoney(int money){
account += money;
//存钱结束后唤醒全部存钱线程
if (account > 200){
notifyAll();
}
System.out.println(Thread.currentThread().getName()+" 余额:<<----- " + getAccount());
}
其他的都不变,运行代码,程序输出:
可以发现,余额不再会低于200了,表明达到我们的效果了。
3 线程join()
关于线程的join的理解是一个难点,我们还是看JDK的解释。
public final void join()
throws InterruptedException
Waits for this thread to die.
关于这里的this就是指调用join()的线程对象。因为join()是一个实例方法。意思是等待调用join()线程对象结束。例如:t.join()用于阻塞调用join()方法的线程,直到t运行结束。
注意:一般t.join()用于在主线程调用。用于按照一定的顺序组织多个线程的运行
例子:假设有1,2,3,4四个线程,我现在想让他们按照3,1,2,4顺序运行,则可以使用join来实现。
/**
* Created by qiyei2015 on 2017/2/6.
*/
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
int i = 0;
while (i < 5){
i++;
String name = Thread.currentThread().getName();
System.out.println(name + " 运行....." + i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试代码:
private static void testJoin(){
MyThread thread1 = new MyThread("线程1");
MyThread thread2 = new MyThread("线程2");
MyThread thread3 = new MyThread("线程3");
MyThread thread4 = new MyThread("线程4");
try {
thread3.start();
thread3.join();
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread4.start();
thread4.join();
System.out.println("线程全部运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果如下:
好,关于本节内容就完成了,下一节会讲JVM内存模型与线程可见性问题