1.多个线程访问同一资源时会产生线程安全问题,因此要实现同步互斥访问资源。
举例:在购票时,总票数10,售票员1卖一张票票数为9,但在售票员1卖这张票的过程中,售票员2也在卖票其开始读到总票数也为10,卖一张9,然后两个售票员都读到了余票为9的错误的信息。
2.解决线程安全问题方式:
(1)synchronized同步方法或同步块(2)lock锁
在java中,每个对象都有一个锁标记,当线程要访问被synchronized关键字修饰的代码片段时,必须检查该对象的锁是否可用,可用,获得锁,执行代码,释放锁。其他想访问该代码片段没获得该锁的线程就处于自我阻塞状态。
<1>synchronized 同步方法
未使用synchronized修饰:
package day03;
import java.util.ArrayList;
/**
* @author wangpei
* @version 创建时间:2017年2月10日 上午10:38:22 类说明
*/
public class Test implements Runnable {
final InsertData insertData = new InsertData();
public void run() {
insertData.insert(Thread.currentThread());
}
public static void main(String[] args) {
Test t = new Test();
new Thread(t, "Thread-0").start();
new Thread(t, "Thread-1").start();
}
}
class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
public void insert(Thread thread) {
for (int i = 0; i < 5; i++) {
System.out.println(thread.getName() + "在插入数据" + i);
arrayList.add(i);
}
}
}
执行结果:
Thread-0在插入数据0
Thread-1在插入数据0
Thread-1在插入数据1
Thread-0在插入数据1
Thread-1在插入数据2
Thread-0在插入数据2
Thread-1在插入数据3
Thread-0在插入数据3
Thread-1在插入数据4
Thread-0在插入数据4
线程0和线程1的执行是无序的
使用synchronized:
class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
public synchronized void insert(Thread thread) {
for (int i = 0; i < 5; i++) {
System.out.println(thread.getName() + "在插入数据" + i);
arrayList.add(i);
}
}
}
执行结果:
Thread-0在插入数据0
Thread-0在插入数据1
Thread-0在插入数据2
Thread-0在插入数据3
Thread-0在插入数据4
Thread-1在插入数据0
Thread-1在插入数据1
Thread-1在插入数据2
Thread-1在插入数据3
Thread-1在插入数据4
Thread-0先获得对象的锁然后,执行代码插入数据,然后释放锁,接着Thread-1执行。0,1线程是顺序执行的。
注意:
(1)在并发访问时,要将资源域设置为private,防止其它线程直接操作该资源。
(2)当一个线程在访问该对象的synchronized修饰的方法时,其它线程可以访问该对象的非synchronized修饰的方法,但不可以访问该对象的其他被synchronized修饰的方法。(对象锁只有一个,已经在使用中,必须获得锁在可以访问synchronized修饰的方法)
例子:
对于对象object:有方法
synchronized void s1(){},
synchronized void s2(){},
void s3(){},
当线程Thread-0获得锁而操作s1方法时,线程Thread-1可以访问s3方法,不可以访问s2方法。
<2>synchronized 同步代码块
形式:
synchronized(对象this(当前对象)/属性){}
class InsertData {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
public void insert(Thread thread) {
synchronized(arrayList){
for (int i = 0; i < 5; i++) {
System.out.println(thread.getName() + "在插入数据" + i);
arrayList.add(i);
}
}
}
}
同步代码块,比同步方法范围小,在执行时访问线程Thread-0访问a方法中同步代码块中的内容,而没获得锁的Thread-1仍旧可访问a方法中的非synchronized代码快。
<3>类锁
针对每个类,也有一个锁(作为class对象的一部分)。synchronized static修饰的方法可以在类范围内防止对static数据的并发访问。