线程的同步和死锁
在程序开发中,所有程序都是通过主方法执行的,而主方法本身就属于一个主线程,所以通过主方法创建的新的线程对象都是子线程。
利用子线程可以进行异步的操作处理,这样可以在不影响主线程运行的前提下进行其他操作,程序的执行速度不仅变快了,并且操作起来也不会产生太多的延迟。
同步操作
所谓同步操作就是一个代码中的多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
在Java中可以使用synchronized关键字实现线程的同步,该关键字可以通过以下两种方式进行使用:
1.同步代码块:利用synchronized包装的代码块,但是需要指定同步对象,一般设置为this;
2.同步方法:利用synchronized定义的方法。
例:观察同步代码块
package Project.Study.Multithreading;
class MyThread7 implements Runnable{
private int ticket=10; //一共有10张票
@Override
public void run(){
for (int x=0;x<10;x++){
synchronized(this){ //定义同步代码块
if(this.ticket>0){ //判断当前是否还有剩余票
try {
Thread.sleep(100); //休眠1s,模拟延迟
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖票,ticket="+this.ticket--);
}
}
}
}
}
public class Test7 {
public static void main(String [] args){
MyThread7 mt=new MyThread7();
new Thread(mt,"票贩子A").start(); //启动多线程
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
new Thread(mt,"票贩子D").start();
}
}
//结果:
//票贩子A,卖票,ticket=10
//票贩子A,卖票,ticket=9
//票贩子A,卖票,ticket=8
//票贩子A,卖票,ticket=7
//票贩子A,卖票,ticket=6
//票贩子A,卖票,ticket=5
//票贩子A,卖票,ticket=4
//票贩子A,卖票,ticket=3
//票贩子A,卖票,ticket=2
//票贩子A,卖票,ticket=1
例:使用同步方法解决问题
package Project.Study.Multithreading;
class MyThread8 implements Runnable{
private int ticket=10; //一共有10张票
@Override
public void run(){
for (int x=0;x<20;x++){
this.sale(); //卖票操作
}
}
public synchronized void sale(){//同步方法
if (this.ticket>0){ //判断当前是否还有剩余票
try{
Thread.sleep(100);//休眠1s,模拟延迟
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",卖票,ticket="+this.ticket--);
}
}
}
public class Test8 {
public static void main(String []args){
MyThread8 mt=new MyThread8();
new Thread(mt,"票贩子A").start();//启动多线程
new Thread(mt,"票贩子B").start();
new Thread(mt,"票贩子C").start();
new Thread(mt,"票贩子D").start();
}
}
//结果:
//票贩子A,卖票,ticket=10
//票贩子A,卖票,ticket=9
//票贩子A,卖票,ticket=8
//票贩子A,卖票,ticket=7
//票贩子A,卖票,ticket=6
//票贩子A,卖票,ticket=5
//票贩子A,卖票,ticket=4
//票贩子A,卖票,ticket=3
//票贩子A,卖票,ticket=2
//票贩子A,卖票,ticket=1
注意:
1.method、static、native、synchronized都不能和"abstract"同时声明方法。
2.当一个线程进入一个对象synchronized方法后,其他线程不能访问此对象的其他方法。
3.同步的代码性能会较低,但是数据的安全性会高(线程安全性高);异步的代码则往往更有效率,不过线程安全性则较低(其数据多是共享数据,即被其它线程使用过了)。
死锁
过多的同步操作有可能会带来死锁问题,导致程序进入停滞状态。所谓死锁就是指两个线程都在等待彼此先完成,造成了程序的停滞状态,一般程序的死锁都是在程序运行时出现的。
例:程序死锁操作
package Project.Study.Multithreading;
class A{
public synchronized void say(B b){
System.out.println("A先生说:把你的笔记本给我,我给你笔,否则不给!");
b.get();
}
public synchronized void get(){
System.out.println("A先生:得到了笔记本,付出了笔,还是什么都干不了!");
}
}
class B{
public synchronized void say(A a){
System.out.println("B先生说:把你的笔给我,我给你笔记本,否则不给!");
a.get();
}
public synchronized void get(){
System.out.println("B先生:得到了笔,付出了笔记本,还是什么都干不了");
}
}
public class Test9 implements Runnable{
private static A a=new A(); //定义类对象
private static B b=new B();
public static void main(String[]args){
new Test9(); //实例化本类对象
}
public Test9(){ //构造方法
new Thread(this).start(); //启动线程
b.say(a); //互相引用
}
@Override
public void run(){
a.say(b); //互相引用
}
}
//结果:
//B先生说:把你的笔给我,我给你笔记本,否则不给!
//A先生说:把你的笔记本给我,我给你笔,否则不给!
//(程序将不会再向下执行,并且不会退出,此为死锁情况出现)
上程序由于两个类都使用了同步的方法定义,就会造成a对象等待b对象执行完毕,而b对象等待a对象执行完毕,这样就会出现死锁现象。