多个线程同时读写共享变量时,会出现数据不一致的问题。这时就需要解决线程同步的问题。
public class Main{
public static void main(String[] args) throws Exception{
var add=new AddTread();
var add=new DecThread();
add.start();
dec.start();
add.join();
dec.join();
System.out.println(Counter.count);
}
}
class Counter{
public static int count=0;
}
class AddThread extends Thread{
public void run(){
for(int i=0;i<1000;i++){
Counter.count+=1;}}
}
class DecThread extends Thread{
public void run(){
for(int i=0;i<1000;i++){
Counter.count-=1}}
}
两个线程同时对一个共享变量进行操作,一个加1000次,一个减1000次。在不加锁的情况下,结果不为0;要保证结果正确,必须让操作是原子操作,原子操作是指不能被中断的一个或者一系列操作。即当一个线程进行加操作的时候,另一个必须等待,等加操作完成之后在进行减操作。
加锁和解锁
保证一段代码的原子性就是通过加锁和解锁实现。Java程序使用synchronized关键字对一个对象进行加锁
sysnchronized(lock){
n=n+1;
}
syschronized关键字保证了代码块在任意时刻只能有一个线程能执行,
将上面操作进行加锁之后
public class Main{
public void static main(String[] args)throws Excetion{
var add=new AddThread();
var dec=neww DecThread();
add.start;
dec.start;
add.join();
dec.join();
System.out.println(Counter.count)
}
}
class Counter{
public static final Object lock =new Object();
public static int count=0;
}
class AddThread extends Thread{
public void run(){
for(int i=0;i<1000;i++){
syschronized(Counter.lock){
Counter.count+=1;}
}}
}
class DecThread extends Thread{
public void run(){
for(int i=0;i<1000;i++){
syschronized(Counter.lock){//获取锁
Counter.count+=1;}//释放锁
}
}
}
在获取锁和释放锁之间的代码块为原子操作。
不需要syschronized的操作
JVM定义了几种原子操作:
基本类型:int n=m
引用类型赋值,Listlist=anotherList
正确的同步方法
在进行加锁的时候,更好的办法是将加锁方法封装起来。
public class Mian{
public static void main(String[] args){
var c1=Counter();
var c2=Counter();
// 对c1进行操作的线程:
new Thread(() -> {
c1.add();
}).start();
new Thread(() -> {
c1.dec();
}).start();
// 对c2进行操作的线程:
new Thread(() -> {
c2.add();
}).start();
new Thread(() -> {
c2.dec();
}).start();
}
}
public class Counter{
private int count=0;
public void add(int n){
syschronized(this){//获取锁,锁住的是自己对象,当调用add方法的时候,就会自动获取当前对象的锁。
count+=n}//释放锁
}
public void dec(int n){
syschronize(this){//获取当前对象锁
count+=1;}//释放当前对象锁
}
public int get(){
return count;}
}
ReadWriteLock
这种锁允许多个线程同时读,当只要有一个线程在写,其他线程就必须等待。
只允许一个线程写入(其他线程既不能读,也不能写)。
没有写入时,多个线程可以同时读(提高性能)
public class Counter{
private final ReadWriteLock rwlock=new ReentrantReadWriteLock();
private final Lock rlock=rwlock.readLock();
private final Lock wlock =rwlock.writeLock();
private int[] counts=new int[10];
public void inc(int index){
wlock.lock();//加写锁
try{
counts[index]+=1;
}finally{
wlock.unlock();//释放写锁
}
}
public int[] get(){
rlock.lock();//加读锁
try{
return Arrays.copyOf(counts,counts.length);}
finally{
rlock.unlock();//释放读锁}
}
}
参考廖雪峰教程