您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
【第9章:多线程】_同步与死锁
发布时间:2020-12-21 17:37:59编辑:雪饮阅读()
正常多线程卖票类的实现
class MyThread implements Runnable{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
if(ticket>0){
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
public class Hello{
public static void main(String args[]){
MyThread m1=new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
D:\>javac Hello.java
D:\>java Hello
卖票:ticket=5
卖票:ticket=2
卖票:ticket=1
卖票:ticket=3
卖票:ticket=4
加延迟就出错了
class MyThread implements Runnable{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
if(ticket>0){
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("延迟异常");
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
public class Hello{
public static void main(String args[]){
MyThread m1=new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
D:\>javac Hello.java
D:\>java Hello
卖票:ticket=5
卖票:ticket=4
卖票:ticket=3
卖票:ticket=2
卖票:ticket=1
卖票:ticket=0
卖票:ticket=-1
这里出现了负数的票数,假定线程A发现票数大于0,假定是1,此时它休眠了1秒,而此时休眠的这1秒的时候线程B也按照程序流程B正好是从它发现是2的时候休眠刚结束,然后B就减去1,那么按照B休眠之前应该是2,但此时就变成了0,那么此时A又苏醒了,A也减去1,按照原本A的流程应该是减去之后是0,结果由于B刚才的操作之后它才操作的,所以这里变成负数了。
synchronized
synchronized可以使得代码块或方法变成同步的
同步代码块
对于代码块则用synchronized声明的”{}”括起来并且synchronized接收一个参数就是线程对象,所以一般都是用this传参
class MyThread implements Runnable{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
synchronized(this){
if(ticket>0){
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("延迟异常");
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
}
}
}
public class Hello{
public static void main(String args[]){
MyThread m1=new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
D:\>javac Hello.java
D:\>java Hello
卖票:ticket=5
卖票:ticket=4
卖票:ticket=3
卖票:ticket=2
卖票:ticket=1
同步方法
synchronized也可以像权限修饰符一样,对于方法只需要修饰即可,不需要额外传参
class MyThread implements Runnable{
private int ticket=5;
public void run(){
for(int i=0;i<100;i++){
this.sale();
}
}
public synchronized void sale(){
if(ticket>0){
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("延迟异常");
e.printStackTrace();
}
System.out.println("卖票:ticket="+ticket--);
}
}
}
public class Hello{
public static void main(String args[]){
MyThread m1=new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
D:\>javac Hello.java
D:\>java Hello
卖票:ticket=5
卖票:ticket=4
卖票:ticket=3
卖票:ticket=2
卖票:ticket=1
不管是同步方法或者是同步代码块,可以看到只要线程变成同步的了,那么就不会出现票被超卖的现象。
实际上不仅仅是票被超卖的情况,任何多线程对于同一个共享资源进行处理的时候都要尽量用同步,这样程序才会更稳健更安全,同时性能就会有所折损。。。
死锁
线程同步过多使用有可能会导致死锁,尤其是两个线程互相依赖的时候。比如有个情景
张三向李四要一幅画,李四说你给我你的一本书我才能给你画。
同样的李四向张三要书,张三说你给我一幅画我才能给你书。
那么实现多线程同步的实现逻辑大概如:
class ZhangSan{
public void giveBook(){
System.out.println("张三给书");
}
public void getHua(){
System.out.println("张三获取画");
}
}
class LiSi{
public void giveHua(){
System.out.println("李四给画");
}
public void getBook(){
System.out.println("李四获取书");
}
}
public class Hello implements Runnable{
//张三与李四总有一个先妥协
private boolean flag=false;
private static ZhangSan zs=new ZhangSan();
private static LiSi ls=new LiSi();
public void run(){
if(flag){
//张三给书之后等待李四"线程"处理结束可以拿画
synchronized(zs){
zs.giveBook();
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
synchronized(ls){
zs.getHua();
}
}
}
else{
//李四给画之后等等张三"线程"处理结束可以拿书
synchronized(ls){
ls.giveHua();
try{
Thread.sleep(500) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
synchronized(zs){
ls.getBook();
}
}
}
}
public static void main(String args[]){
Hello zsThread=new Hello();
Hello lsThread=new Hello();
zsThread.flag=false;
lsThread.flag=true;
Thread zsT=new Thread(zsThread);
Thread lsT=new Thread(lsThread);
zsT.start();
lsT.start();
}
}
然后出现死锁后这里表现的就是光标定在这里不断闪烁,程序一直处于等待状态。
关键字词:java,同步,死锁