我们先看个错误示例。代码功能:声明一个数字并赋值10000.然后让1w个线程去减少1,1w个线程去增加1。理论上说,加一万减一万,最后数字的结果并不会改变。
代码:
代码语言:javascript复制class ErrorDemo{
private static int Num=10000;
public static void main(String[] args) throws InterruptedException {
int n=10000;
//声明线程数组,在后面使用join让主线程等待所有线程执行完。
// 不然主线程跑完了,其他线程没执行完就输出结果会不对的。
Thread[] add=new Thread[n];
Thread[] reduce=new Thread[n];
//创建1w个减少线程
for(int i=0;i<n;i ){
Thread t1=new Thread(){
public void run(){
Num-=1;
}
};
t1.start();
reduce[i]=t1;
}
//创建1w个增加线程
for(int i=0;i<n;i ){
Thread t2=new Thread(){
public void run(){
Num =1;
}
};
t2.start();
add[i]=t2;
}
//让主线程等待所有线程执行完毕
for(Thread t:add)
{
t.join();
}
for(Thread t:reduce)
{
t.join();
}
//输出结果
System.out.println(Num);
}
}
最后输出的结果是9999。这个结果不固定,有多有少。
造成这种错误的原因是:
假设增加线程获取到数字是10000,进行了加一操作,结果是10001。
但是减少线程在返回结果之前也获取到了数字10000。
减少线程最后返回9999。
所以我们原本期望得到的10000变成了9999。
为了解决这种问题,可以使用synchronized
使用方法:
代码语言:javascript复制Object object=new Object();//object就是你当前线程操作的对象,比如上面的int数字
synchronized (object){
//当前线程独占了object,其他线程访问object就会等待当前线程释放object
}
释放object的方法:
代码语言:javascript复制synchronized代码块结束或者异常抛出。
使用synchronized后的代码
代码语言:javascript复制public class Thread_synchronization {
}
class ErrorDemo{
public static void main(String[] args) throws InterruptedException {
//因为synchronized里面要求的是对象,所以需要用Integer声明
Integer Num = 10000;
int n=10000;
//声明线程数组,在后面使用join让主线程等待所有线程执行完。
// 不然主线程跑完了,其他线程没执行完就输出结果会不对的。
Thread[] add=new Thread[n];
Thread[] reduce=new Thread[n];
//创建1w个减少线程
for(int i=0;i<n;i ){
Thread t1=new Thread(){
public void run(){
synchronized (Num) {
addNum(Num);
}
}
};
t1.start();
reduce[i]=t1;
}
//创建1w个增加线程
for(int i=0;i<n;i ){
Thread t2=new Thread(){
public void run(){
synchronized (Num) {
reduceNum(Num);
}
}
};
t2.start();
add[i]=t2;
}
//让主线程等待所有线程执行完毕
for(Thread t:add)
{
t.join();
}
for(Thread t:reduce)
{
t.join();
}
//输出结果
System.out.println(Num);
}
public static int addNum(int Num){
return Num 1;
}
public static int reduceNum(int Num){
return Num-1;
}
}
最后的结果是10000
除了上面的方法,还可以直接在操作数据的方法前加上synchronized
代码语言:javascript复制public class Thread_synchronization {
}
class ErrorDemo{
public static void main(String[] args) throws InterruptedException {
//因为synchronized里面要求的是对象,所以需要用Integer声明
Integer Num = 10000;
int n=10000;
//声明线程数组,在后面使用join让主线程等待所有线程执行完。
// 不然主线程跑完了,其他线程没执行完就输出结果会不对的。
Thread[] add=new Thread[n];
Thread[] reduce=new Thread[n];
//创建1w个减少线程
for(int i=0;i<n;i ){
Thread t1=new Thread(){
public void run(){
addNum(Num);
}
};
t1.start();
reduce[i]=t1;
}
//创建1w个增加线程
for(int i=0;i<n;i ){
Thread t2=new Thread(){
public void run(){
reduceNum(Num);
}
};
t2.start();
add[i]=t2;
}
//让主线程等待所有线程执行完毕
for(Thread t:add)
{
t.join();
}
for(Thread t:reduce)
{
t.join();
}
//输出结果
System.out.println(Num);
}
public synchronized static int addNum(int Num){
return Num 1;
}
public synchronized static int reduceNum(int Num){
return Num-1;
}
}
最后的结果是10000
如果一个类里面的所有方法都被synchronized修饰,那么这个类就是线程安全的类。
同一时间,只有一个线程可以进入这个类的一个实例去修改数据,以免多个线程同时修改数据,而产生脏数据。