线程同步
一.使用synchronized关键字
由于每个java对象都有一个内置锁,用synchronized修饰方法或者代码块时,内置锁会保护整个方法或代码块,要想执行这个方法或者代码块必须获得其内置锁,运行时会加上内置锁,当运行结束时,内置锁会打开。由于同步是一种高开销的工作,所以尽量减少同步的内容,只需同步代码块就可以。(ps:synchronized是不能锁住不同对象的线程的,只能锁住同一个对象的线程,也就是说锁住的是方法所属的主体对象自身)
1.修饰方法
public class Sy_Thread_Demo1 implements Runnable {
static int num = 10000;
//修饰方法加同步锁synchronized
@Override
public synchronized void run() {
while(true){
if(num>=0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行第"+num--+"次线程!");
}else{
break;
}
}
}
}
public class Sy_Test {
public static void main(String[] args) {
Sy_Thread_Demo1 st = new Sy_Thread_Demo1();
Thread t1 = new Thread(st);
t1.setName("线程1");
Thread t2 = new Thread(st);
t2.setName("线程2");
t1.start();
t2.start();
}
}
2.修饰代码块
public class Sy_Thread_Demo2 implements Runnable{
static int num = 10000;
@Override
public void run() {
synchronized (this){
while(true){
if(num>=0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在运行第"+num--+"次线程!");
}else{
break;
}
}
}
}
}
public class Sy_Test2 {
public static void main(String[] args) {
Sy_Thread_Demo2 st = new Sy_Thread_Demo2();
Thread t1 = new Thread(st);
t1.setName("线程1");
Thread t2= new Thread(st);
t2.setName("线程2");
t1.start();
t2.start();
}
}
二.wait与notify
- wait(),使一个线程处于等待状态,并释放所持对象的锁,与sleep不同,sleep不会释放对象锁。
- notify(),唤醒一个处于阻塞状态的线程,进入就绪态,并加锁,只能唤醒一个线程,但不能确切知道唤醒哪一个,由JVM决定,不是按优先级。其实不是对对象锁的唤醒,是告诉调用wait方法的线程可以去竞争对象锁了。wait和notify必须在synchronized代码块中调用。
- notifyAll(),唤醒所有处于阻塞状态的线程,并不是给他们加锁,而是让他们处于竞争。
为什么wait和notify要在synchronized代码块中使用
调用wait()就是释放锁,释放锁的前提是必须要先获得锁,先获得锁才能释放锁,释放锁后进入等待队列。
notify(),notifyAll()是将锁交给含有wait()方法的线程,让其继续执行下去,如果自身没有锁,怎么叫把锁交给其他线程呢;(本质是让处于阻塞队列的线程进入等待队列竞争锁)