线程同步两大方式
1、synchronized:内键锁,JDK1.0作为关键字提供的同步手段
1)同步代码块(推荐使用,锁粒度较细)
使用内键锁作用于方法中;
同一时刻只能有一个线程进入的代码块,方法内仍是多线程
synchronized(this){
//需要同步的代码块
}
2)同步方法
使用内键锁作用于方法头;
同一个时刻只有一个线程能进入此方法
public synchronized void fun(){
}
3)对象锁与全局锁
对象锁:
以上两种形式(非静态方法),只能防止多个线程同时执行同一个对象的同步代码块,所以,synchronized是对象锁,锁的是对象本身,即为this
全局锁:
全局锁锁的是真正的代码段,与对象无关
实现全局锁的两种方式:
①使用静态的同步方法
②使用synchronized关键字锁住线程类:在同步代码块锁住Class对象(MyThread.class)
4)代码应用:
①synchronized对象锁
class MyThread implements Runnable{
@Override
public void run() {
// MyThread myThread = new MyThread();
// myThread.fun();
// //结果没达到预想,原因:
// //每个线程开启,调用run方法,都有一个myThread对象,相当于this
// //所以synchronized在普通方法中锁的仅仅是this对象
//修改
this.fun();
}
public synchronized void fun() {
System.out.println("start");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println("end");
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
for(int i = 0; i < 3; i++) {
new Thread(myThread1).start();
}
}
}
②线程0在testA方法中,线程1就不能再进入testB方法
原因:由于synchronized锁的普通方法实质锁的是对象,而线程0和线程1是同一个对象threadTest。
class MyThread2 implements Runnable {
private ThreadTest test;
public MyThread2(ThreadTest test) {
super();
this.test = test;
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
this.test.testA();
}else if (Thread.currentThread().getName().equals("Thread-1")) {
this.test.testB();
}
}
}
class ThreadTest {
public synchronized void testA() {
System.out.println(Thread.currentThread().getName()+"testA方法");
while(true) {}
}
public synchronized void testB() {
System.out.println(Thread.currentThread().getName()+"testB方法");
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
ThreadTest threadTest = new ThreadTest();
MyThread2 myThread = new MyThread2(threadTest);
Thread thread0 = new Thread(myThread);
Thread thread1 = new Thread(myThread);
thread0.start();//线程0在testA方法中,线程1就不能再进入testB方法
Thread.sleep(1000);
thread1.start();
}
}
2、Lock锁:对象锁,JDK1.5
使用Lock锁需要显示的加锁和解锁
Lock lock = ...;
lock.lock();
try{
}finally{
lock.unlock;//释放锁
}
解锁操作需要放在finally块里,防止锁被超界获取
获取锁的操作不能放在try块里,因为Lock是可重入锁,如果外层也已经调用lock(),而里层因为调用lock()抛出异常然后调用unlock(),外层不能知道,导致外层代码无法正确同步