zoukankan      html  css  js  c++  java
  • 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步

    • 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条线程访问,一条线程在执行一个循环的过程中被中断,下一个线程则出现错误
    • 因此,线程任务中可能引起错误的地方应当被一次执行完毕

    同步代码块

    • 用同步代码块改写上面的代码
    package testpack;
    
    public class Test1  { 
        public static void main(String[] args){ 
        	System.out.println("现在是主线程: "+Thread.currentThread()); 
        	System.out.println("下面新建两个线程");
        	A a=new A(500); 
        	new Thread(a,"线程A").start(); 
        	new Thread(a,"线程B").start();
        	new Thread(a,"线程C").start();
        }
    }
    class A implements Runnable{
    	private int tickets;
    	A (int tick){
    		tickets=tick;
    	}
    	private Object obj=new Object();              //同步监视器
    	public void run() {
    		synchronized(obj){                        //同步代码块
    			for (;tickets>0;tickets--) {
    				System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
    				try{
    					Thread.sleep(1);              //让当前线程暂停1毫秒,其他线程也不能执行该同步代码块
    				}catch(InterruptedException ex){
    					ex.printStackTrace();
    				}
    				if (tickets==1){
    					System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
    				}
    			}
    		}
    		
    	}
    }
    
    • 同步监视器,就是一个普通的对象,就像一把锁,只有获得了同步监视器的线程才能执行同步代码块。
    • 同步代码块执行一次完毕后,将会释放锁,接下来是这条线程拿到同步锁,还是其他其他线程,则不一定,根据线程调度而定,但是在同步代码块执行过程中,不会被中断,一个同步任务会被一次执行完毕

    同步方法

    • 在同步代码块中,synchonized关键字在run()方法内部,修饰的是一段代码,也可以用来修饰run()方法,也就是同步方法
    • synchronized不只可以修饰run()方法,还可以修饰其他方法,只要是需要一次同步完成的任务,然后再在run()方法中被调用
    • 同步方法中有一个隐式的同步监视器,就是this,也就是调用run()方法(或同步方法)的这个对象
    • 还是上面的实例,用同步方法改写
    package testpack;
    
    public class Test1  { 
        public static void main(String[] args){ 
        	System.out.println("现在是主线程: "+Thread.currentThread()); 
        	System.out.println("下面新建两个线程");
        	A a=new A(500);
        	new Thread(a,"线程A").start();
        	new Thread(a,"线程B").start();
        	new Thread(a,"线程C").start();
        }
    }
    class A implements Runnable{
    	private int tickets;
    	A (int tick){
    		tickets=tick;
    	}
    	public synchronized void run() {                                    //同步方法
    			for (;tickets>0;tickets--) {
    				System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
    				try{
    					Thread.sleep(1);
    				}catch(InterruptedException ex){
    					ex.printStackTrace();
    				}
    				if (tickets==1){
    					System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
    				}
    			}
    		
    	}
    }
    

    释放同步监视器

    • 当前线程的同步任务(同步方法、同步代码块)执行完毕
    • 在同步任务中,遇到break、return,终止了同步任务
    • 在同步任务中,出现Error、Exception等,导致同步任务结束
    • 在同步任务中,执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器
    • 不会释放同步监视器的情况:
      • 同步任务中,调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行
      • 同步任务中,其他线程调用了该线程的suspend()方法将该线程挂起

    同步锁

    • 除了可以用new Object()和this作同步监视器往外,还可以定义专门的同步锁,且功能更加强
    • Lock接口
      • ReentrantLock实现类
    • ReadWriteLock
      • ReentrantReadWriteLock实现类
      • ReentrantReadWriteLock.ReadLock
      • ReentrantReadWriteLock.WriteLock
    • StampedLock
    • 示例:用ReentrantLock改写上面的代码
    package testpack;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test1  { 
        public static void main(String[] args){ 
        	System.out.println("现在是主线程: "+Thread.currentThread()); 
        	System.out.println("下面新建两个线程");
        	A a=new A(50);
        	new Thread(a,"线程A").start();
        	new Thread(a,"线程B").start();
        	new Thread(a,"线程C").start();
        }
    }
    class A implements Runnable{
    	private final ReentrantLock lock=new ReentrantLock();    //定义一个同步锁
    	private int tickets;
    	A (int tick){
    		tickets=tick;
    	}
    	public void run() {
    			lock.lock();                                     //加锁
    			for (;tickets>0;tickets--) {
    				System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
    				if (tickets==1){
    					System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
    				}
    			}
    			lock.unlock();                                    //释放锁
    	}
    }
    

    死锁

    • 两个线程各拿一把锁,下一步运行都需要对方手里那把锁,但都拿不到,则造成死锁,程序不能继续执行
    package testpack;
    public class Test1  { 
        public static void main(String[] args){ 
        	DeadLock dl=new DeadLock();
        	new Thread(dl).start();
        	dl.init();
        }
    }
    class DeadLock implements Runnable {
    	A a=new A();
    	B b=new B();
    	public void init(){
    		a.a1(b);
    		System.out.println("进入主线程");
    	}
    	public void run(){
    		b.b1(a);
    		System.out.println("进入子线程");
    	}
    }
    class A {
    	public synchronized void a1(B b){
    		System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行a1()");
    		try{
    			Thread.sleep(10);
    		}catch(InterruptedException ex){
    			ex.printStackTrace();
    		}
    		System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用b2()");
    		b.b2();   //b2方法是同步方法,调用该方法要对调用的对象b加锁
    	}
    	public synchronized void a2(){
    		System.out.println("这是a2()方法");
    	}
    }
    class B{
    	
    	public synchronized void b1(A a){
    		System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行b1()");
    		try{
    			Thread.sleep(10);
    		}catch(InterruptedException ex){
    			ex.printStackTrace();
    		}
    		System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用a2()");
    		a.a2();   //a2方法是同步方法,调用该方法要对调用的对象a加锁
    	}
    	
    	public synchronized void b2(){
    		System.out.println("这是b2()方法");
    	}
    }
    
    • 上面在调用a.a2()和b.b2()方法时,分别要对a对象和b对象加锁,但这时,a、b对象的锁都在对方手里,造成两个线程阻塞

    其他

    • 可变类的线程安全是以降低程序的运行效率为代价的
    • 不要对线程安全类的所有方法都进行同步,只对那些改变共享资源的方法进行同步
    • 如果一个类有单线程和多线程运行环境,那么应该提供两种版本,就是StringBuilder(单线程)和StringBuffer(多线程)一样
  • 相关阅读:
    UVa 116 单向TSP(多段图最短路)
    POJ 1328 Radar Installation(贪心)
    POJ 1260 Pearls
    POJ 1836 Alignment
    POJ 3267 The Cow Lexicon
    UVa 1620 懒惰的苏珊(逆序数)
    POJ 1018 Communication System(DP)
    UVa 1347 旅行
    UVa 437 巴比伦塔
    UVa 1025 城市里的间谍
  • 原文地址:https://www.cnblogs.com/sonng/p/6134444.html
Copyright © 2011-2022 走看看