目录
一、引言
先介绍两个概念
线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。
同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。
为什么使用同步?
多线程给我们带来了很大的方便,但是同时也给我们带来了一个致命的问题,当我们对线程共享数据进行非原子操作时,会带来知名的错误。当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。这就是多线程同步提上议程的原因,解决多线程安全问题。
举个例子:假设银行里某一用户账户有1000元,线程A读取到1000,并想取出这1000元,并且在栈中修改成了0但还没有刷新到堆中,线程B也读取到1000,此时账户刷新到银行系统中,则账户的钱变成了0,这个时候也想去除1000,再次刷新到行系统中,账号的钱变成0,这个时候A,B都取出1000元,但是账户只有1000,显然出现了问题。针对上述问题,假设我们添加了同步机制,那么就可以很容易的解决。
tips:java1.5新引入了concurrent包,里面都是关于并发的,里面还有locks和atomic两个包,很值得学习,使用方便。
二、synchronize同步
1、synchronize代码块
这里举了个复杂点的例子,模仿的生产者消费者的模式,每次添加一个数,然后读出一个数,添加后不能再次添加,读出后不能再次读出。synchronize(this){}中this为同步锁,可以是任意对象,这里是用的该类本身,只要每次锁一样就能保证两个线程再synchronize代码块中的执行是同步的,所以每次只能读取或者添加。
wait和notify是线程等待和唤醒线程的意思,每当线程wait后被notify会从等待位置继续执行,使用这个是为了在线程中进行通信,使多个线程按照自己的想法有序执行(当添加数据线程添加后在添加该线程就会wait,当读取数据线程读取数据后就会notify添加线程继续执行)。
创建多线程类
package com.lock;
public class Testlock {
public static void main(String [] args)
{
data();
}
/***
* synchronize
*/
static void data()
{
final DataSynchronize data=new DataSynchronize();
new Thread(new Runnable() {
public void run() {
for(int i=0;i<1000000000;i++)
{
data.write();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=0;i<1000000000;i++)
{
data.get();
}
}
}).start();
}
}
封装的共享数据及方法类
package com.lock;
/**
* synchronize
* 两个线程,一个存数据一个取数据——多个线程全部互斥
*/
class DataSynchronize {
boolean hasdata;
int x;
public void write()
{
synchronized (this)
{
//有数据
if (hasdata==true)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没数据
System.out.println(Thread.currentThread().getName()+"准备添加数据");
x=(int)(Math.random()*100000);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"添加数据"+x);
//通过上述步骤有了数据,并唤醒取数据
hasdata=true;
this.notify();
}
}
public synchronized void get()
{
synchronized (this)
{
//没有数据
if (hasdata==false)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"准备读取数据");
//x=(int)Math.random();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"读取数据为"+x);