zoukankan      html  css  js  c++  java
  • Java学习线程安全

    线程同步(线程安全处理Synchronized)

    线程同步的两种方式:

    1、同步代码块

    2、同步方法

    同步代码块

    同步代码块: 在代码块声明上 加上synchronized

    synchronized (锁对象) {

    可能会产生线程安全问题的代码

    }

    同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    package com.oracle.demo01;
    
    /*格式:
     * synchronized(任意对象){
     *    线程要操作的共享数据 
     * }
     *  任意对象:同步对象、同步锁、对象监视器
     *  同步怎么能保证安全性?
     *  没有锁的线程不能执行,只能等待
     *  
     *  线程获得CPU资源以后想要执行同步代码块的内容,它先去看一下同步锁有没有
     *  如果有,那么你的线程获得锁,然后进入同步代码块执行代码,虽然在同步代码块中,线程休眠了
     *  在此线程休眠时,其他线程获得资源想要执行同步代码块,它先去看一看有没有锁,一看,没有锁
     *  它就被阻挡在代码块之后,只有等
     *  休眠的线程睡完了,起来继续执行代码块中的内容,全部执行完代码块中的内容,就释放锁
     *  然后其他线程获得锁,就可以继续执行了
     *  
     *  线程安全了,那么执行速度就会变慢了
     * */
    public class Ticket implements Runnable {
    	private int ticket = 100;
    	Object obj = new Object();
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while (true) {
    			synchronized (obj) {
    				if (ticket > 0) {
    					try {
    						Thread.sleep(10);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    					System.out.println(Thread.currentThread().getName() + "出售第" + (ticket--) + "张票");
    				}
    			}
    
    		}
    	}
    
    }
    
    package com.oracle.demo01;
    
    public class Test {
    	public static void main(String[] args) {
    		Ticket t = new Ticket();
    		Thread t0 = new Thread(t);
    		Thread t1 = new Thread(t);
    		Thread t2 = new Thread(t);
    		t0.start();
    		t1.start();
    		t2.start();
    	}
    }

    同步方法

    同步方法:在方法声明上加上synchronized

    public synchronized void method(){

        可能会产生线程安全问题的代码

    }

    同步方法中的锁对象是 this

    静态同步方法: 在方法声明上加上static synchronized

    public static synchronized void method(){

    可能会产生线程安全问题的代码

    }

    package com.oracle.demo02;
    
    /*
     *同步方法中有锁么?
     *有锁,同步方法中的对象锁,就是本类对象引用 this
     *StringBuffer:之所以安全,就是因为里面有同步方法,只要有同步,就安全,就慢
     *StringBuilder:不安全,就是因为没有同步,就快
     *
     *如果你的同步方法时静态的,还有锁么?还是this么?
     *有锁,不是this,是本类自己,也就是ticket.class   本类类名.class
     * */
    public class Ticket implements Runnable {
    	private int ticket = 100;
    	Object obj = new Object();
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while (true) {
    			//synchronized (this)
    			synchronized (obj) {
                   method();
    			}
    
    		}
    	}
       //同步代码块,代码更简洁
    	public synchronized void method() {
    		if (ticket > 0) {
    			try {
    				Thread.sleep(10);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "出售第" + (ticket--) + "张票");
    		}
    	}
    }
    
    package com.oracle.demo01;
    
    public class Test {
    	public static void main(String[] args) {
    		Ticket t = new Ticket();
    		Thread t0 = new Thread(t);
    		Thread t1 = new Thread(t);
    		Thread t2 = new Thread(t);
    		t0.start();
    		t1.start();
    		t2.start();
    	}
    }

    Lock接口

    Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

    常用方法:

    package com.oracle.demo03;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    /*
     * JDK5以后出现Lock接口的可以代替同步关键字
     * 功能是一样的,但是更灵活
     * */
    public class Ticket implements Runnable {
    	private int ticket = 100;
    	//接口不能实例化对象,就用子类的引用
    	private Lock l=new ReentrantLock();
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while (true) {
    			l.lock();	
    				if (ticket > 0) {
    					try {
    						Thread.sleep(10);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    					System.out.println(Thread.currentThread().getName() + "出售第" + (ticket--) + "张票");
    				}else{
    					return;
    				}
    				l.unlock();
    			}
    
    		}
    }
    package com.oracle.demo03;
    
    public class Test {
    	public static void main(String[] args) {
    		Ticket t = new Ticket();
    		Thread t0 = new Thread(t);
    		Thread t1 = new Thread(t);
    		Thread t2 = new Thread(t);
    		t0.start();
    		t1.start();
    		t2.start();
    	}
    }
    

    死锁

    死锁的前提就是:一个线程必须同时拥有两个对象的资源才能执行程序。
    1.线程1 首先占有对象1,接着试图占有对象2
    2. 线程2 首先占有对象2,接着试图占有对象1
    3. 线程1 等待线程2释放对象2
    4. 与此同时,线程2等待线程1释放对象1
    就会。。。一直等待下去,直到天荒地老,海枯石烂,山无棱 ,天地合。。。
    package com.oracle.demo04;
    
    public class LockA {
    	private LockA() {
    
    	}
        //保证A锁的唯一性
    	public final static LockA la = new LockA();
    
    }
    package com.oracle.demo04;
    
    public class LockB {
    	private LockB() {
    
    	}
        //保证了B锁的唯一性
    	public final static LockB lb = new LockB();
    }
    package com.oracle.demo04;
    
    public class DeadLock implements Runnable {
    	private int i = 0;
    
    	@Override
    	public void run() {
    		while (true) {
    			if (i % 2 == 0) {
    				synchronized (LockA.la) {
    					System.out.println("if...LockA");
    					synchronized (LockB.lb) {
    						System.out.println("if...LockB");
    					}
    				}
    			} else {
    				synchronized (LockB.lb) {
    					System.out.println("else...LockB");
    					synchronized (LockA.la) {
    						System.out.println("else...LockA");
    
    					}
    				}
    			}
    			i++;
    		}
    
    	}
    
    }
    package com.oracle.demo04;
    
    public class Test {
    	public static void main(String[] args) {
    		DeadLock dl = new DeadLock();
    		Thread t0 = new Thread(dl);
    		Thread t1 = new Thread(dl);
    		t0.start();
    		t1.start();
    	}
    }
    

    等待唤醒机制

    线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

    等待唤醒机制:通过一定的手段使各个线程能够有效的利用资源,该手段就是等待唤醒机制

    等待唤醒机制所涉及到的方法:

    wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中

    notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的

    notifyAll():唤醒全部,可以将线程池中的所有wait()线程都唤醒

    所谓唤醒:就是让线程池中的线程具备执行资格。必须注意:这些方法都是在同步中才有效。同时这些方法在使用时必须表明所属锁,这样才可以明确这些方法操作的是哪个锁上的线程。

    这些方法都被定义在Object类中了,因为这些方法在使用时,必须表明所属锁,而锁又可以是任意的,能被任意对象调用的方法一定定义在Object类中。

    例如:

    输入线程向Resource中输入name ,sex , 输出线程从资源中输出,先要完成的任务是:

    l 1.当input发现Resource中没有数据时,开始输入,输入完成后,叫output来输出。如果发现有数据,就wait();

    l 2.当output发现Resource中没有数据时,就wait() ;当发现有数据时,就输出,然后,叫醒input来输入数据。

    package com.oracle.demo05;
    
    public class Resouse {
        public  String name;
        public  String sex;
        public boolean flag=false;
    }
    //flag标记:为true的时候,赋值完成
    //          为false的时候,获取值完成
    //输入类:true,等待,false,赋值,赋值完成后,把flag变为true   notify输出,自己wait()
    //输出类:false,等待,true,取值,取值完成后,把flag变为false  notify输入,自己wait()
    
    package com.oracle.demo05;
    
    public class Input implements Runnable {
    	private Resouse r;
    
    	public Input(Resouse r) {
    		this.r = r;
    	}
    
    	@Override
    	public void run() {
    		// 往资源对象中输入数据
    		int i = 0;
    		while (true) {
    			//r表明锁的唯一性
    			synchronized (r) {
    				if (r.flag) {
    					try {
    						r.wait();
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				if (i % 2 == 0) {
    					r.name = "张三";
    					r.sex = "女";
    				} else {
    					r.name = "lisi";
    					r.sex = "nan";
    				}
    				r.flag = true;
    				r.notify();
    			}
    			i++;
    		}
    	}
    
    }
    
    package com.oracle.demo05;
    
    public class Output implements Runnable {
    	private Resouse r;
    
    	public Output(Resouse r) {
    		this.r = r;
    	}
    
    	@Override
    	public void run() {
    		// 从资源对象中输出数据
    		while (true) {
    			synchronized (r) {
    				if (!r.flag) {
    					try {
    						r.wait();
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				System.out.println(r.name + "..." + r.sex);
    				r.flag = false;
    				r.notify();
    			}
    
    		}
    	}
    }
    
    package com.oracle.demo05;
    
    
    public class Test {
    	public static void main(String[] args) {
    		Resouse r = new Resouse();
    		Input in = new Input(r);
    		Output out = new Output(r);
    		Thread tin = new Thread(in);
    		Thread tout = new Thread(out);
    		tin.start();
    		tout.start();
    	}
    }
    

      

  • 相关阅读:
    December 23rd 2016 Week 52nd Friday
    December 22nd 2016 Week 52nd Thursday
    December 21st 2016 Week 52nd Wednesday
    December 20th 2016 Week 52nd Tuesday
    December 19th 2016 Week 52nd Sunday
    December 18th 2016 Week 52nd Sunday
    uva294(唯一分解定理)
    uva11624Fire!(bfs)
    fzu2150Fire Game(双起点bfs)
    poj3276Face The Right Way
  • 原文地址:https://www.cnblogs.com/Java-125/p/8949478.html
Copyright © 2011-2022 走看看