Java多线程之同步与死锁
1.线程的同步
1)The code segments within a program that access the same object from separate, concurrent threads are called “critical sections”。这是临界区的概念。
2)同步的两种方式:同步块和同步方法。
3)每一个对象都有一个监视器,或者叫做锁。
当线程执行到synchronized的时候,检查传入的实参对象,并得到该对象的锁旗标。如果得不到,那么此线程就会被加入到一个与该对象的锁旗标相关联的等待线程池中,一直等到对象的锁旗标被归还,池中的等待线程就会得到该锁旗标,然后继续执行下去。当线程执行完成同步代码块,就会自动释放它占有的同步对象的锁旗标。一个用于synchronized语句中的对象称为监视器,当一个线程获得了synchronized(object)语句中的代码块的执行权,即意味着它锁定了监视器。
4)同步方法利用的是this所代表的对象的锁。
下面实例介绍代码块与方法间的同步。观察this的作用。
注2,如果使用synchronized (str),则两个线程不会同步。
5)要时刻考虑CPU会随时切换线程的情况。
6)同步是以牺牲程序的性能为代价的。
1.1同步代码块
ThreadDemo2.java
1.2同步方法
ThreadDemo3.java
2.线程的死锁
线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
track after start
MainThread enter A.foo
track in run
RacingThread enter B.bar
MainThread trying to call B.last
RacingThread trying to call A.last
3.参考资料
[1]Thinking in Java 3rd
1)The code segments within a program that access the same object from separate, concurrent threads are called “critical sections”。这是临界区的概念。
2)同步的两种方式:同步块和同步方法。
3)每一个对象都有一个监视器,或者叫做锁。
当线程执行到synchronized的时候,检查传入的实参对象,并得到该对象的锁旗标。如果得不到,那么此线程就会被加入到一个与该对象的锁旗标相关联的等待线程池中,一直等到对象的锁旗标被归还,池中的等待线程就会得到该锁旗标,然后继续执行下去。当线程执行完成同步代码块,就会自动释放它占有的同步对象的锁旗标。一个用于synchronized语句中的对象称为监视器,当一个线程获得了synchronized(object)语句中的代码块的执行权,即意味着它锁定了监视器。
4)同步方法利用的是this所代表的对象的锁。
下面实例介绍代码块与方法间的同步。观察this的作用。
public class ThreadDemo6 {
public static void main(String[] args) {
ThreadTest t = new ThreadTest();
new Thread(t).start();
try {
Thread.sleep(1);//(1)
} catch (InterruptedException e) {
e.printStackTrace();
}
t.str=new String("method");
new Thread(t).start();
}
}
class ThreadTest implements Runnable {
private int tickets = 100;
private int flag=0;
String str = new String("");
public void run() {
if (str.equals("method")) {
while (flag==0) {
sale();
}
} else {
while (true) {
synchronized (this) {//synchronized (str)//(2)
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else return;
}
}
}
}
public synchronized void sale() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("track in method sale.");
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else flag = 1;
}
}
注1,如果不使主线程sleep,很可能两个新建线程都执行同步方法(sale)中的代码。因为,产生并启动第一个线程,这个线程不见得马上开始运行,CPU可能还在原来的main线程上运行,并将str变量设置为”method”,等到第一个线程真正开始运行时,此刻检查到str的值为”method”,所以它将运行sale方法。public static void main(String[] args) {
ThreadTest t = new ThreadTest();
new Thread(t).start();
try {
Thread.sleep(1);//(1)
} catch (InterruptedException e) {
e.printStackTrace();
}
t.str=new String("method");
new Thread(t).start();
}
}
class ThreadTest implements Runnable {
private int tickets = 100;
private int flag=0;
String str = new String("");
public void run() {
if (str.equals("method")) {
while (flag==0) {
sale();
}
} else {
while (true) {
synchronized (this) {//synchronized (str)//(2)
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else return;
}
}
}
}
public synchronized void sale() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("track in method sale.");
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else flag = 1;
}
}
注2,如果使用synchronized (str),则两个线程不会同步。
5)要时刻考虑CPU会随时切换线程的情况。
6)同步是以牺牲程序的性能为代价的。
1.1同步代码块
ThreadDemo2.java
class ThreadTest implements Runnable {
private int tickets = 100;
String str = new String("");
public void run() {
while (true) {
synchronized (str) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else return;
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args){
ThreadTest t=new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
private int tickets = 100;
String str = new String("");
public void run() {
while (true) {
synchronized (str) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
} else return;
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args){
ThreadTest t=new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
1.2同步方法
ThreadDemo3.java
class ThreadTest implements Runnable {
private int tickets = 100;
private int flag=0;
public void run() {
while (flag==0) sale();
}
public synchronized void sale() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
}else flag=1;
}
}
public class ThreadDemo3 {
public static void main(String[] args){
ThreadTest t=new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
在同一个类中,使用synchronized关键字定义的若干方法,可以在多个线程之间同步,当有一个线程进入了synchronized修饰的方法(获得监视器),其他线程就不能进入同一个对象的所有使用了synchronized修饰的方法,直到第一个线程执行完它所进入的synchronized修饰的方法为止(离开监视器)。private int tickets = 100;
private int flag=0;
public void run() {
while (flag==0) sale();
}
public synchronized void sale() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " is saling ticket " + tickets--);
}else flag=1;
}
}
public class ThreadDemo3 {
public static void main(String[] args){
ThreadTest t=new ThreadTest();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
2.线程的死锁
线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
class A{
synchronized void foo(B b){
String name=Thread.currentThread().getName();
System.out.println(name+" enter A.foo");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+" trying to call B.last");
b.last();
}
synchronized void last(){
System.out.println(Thread.currentThread().getName()
+"inside A.last");
}
}
class B{
synchronized void bar(A a){
String name=Thread.currentThread().getName();
System.out.println(name+" enter B.bar");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+" trying to call A.last");
a.last();
}
synchronized void last(){
System.out.println(Thread.currentThread().getName()
+" inside B.last");
}
}
public class Deadlock implements Runnable{
A a=new A();
B b=new B();
Deadlock(){
Thread.currentThread().setName("MainThread");
new Thread(this).start();
System.out.println("track after start");
a.foo(b);
System.out.println("back in main thread");
}
public void run(){
System.out.println("track in run");
Thread.currentThread().setName("RacingThread");
b.bar(a);
System.out.println("back in other thread");
}
public static void main(String[]args){
new Deadlock();
}
}
结果:synchronized void foo(B b){
String name=Thread.currentThread().getName();
System.out.println(name+" enter A.foo");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+" trying to call B.last");
b.last();
}
synchronized void last(){
System.out.println(Thread.currentThread().getName()
+"inside A.last");
}
}
class B{
synchronized void bar(A a){
String name=Thread.currentThread().getName();
System.out.println(name+" enter B.bar");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+" trying to call A.last");
a.last();
}
synchronized void last(){
System.out.println(Thread.currentThread().getName()
+" inside B.last");
}
}
public class Deadlock implements Runnable{
A a=new A();
B b=new B();
Deadlock(){
Thread.currentThread().setName("MainThread");
new Thread(this).start();
System.out.println("track after start");
a.foo(b);
System.out.println("back in main thread");
}
public void run(){
System.out.println("track in run");
Thread.currentThread().setName("RacingThread");
b.bar(a);
System.out.println("back in other thread");
}
public static void main(String[]args){
new Deadlock();
}
}
track after start
MainThread enter A.foo
track in run
RacingThread enter B.bar
MainThread trying to call B.last
RacingThread trying to call A.last
3.参考资料
[1]Thinking in Java 3rd