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();
    	}
    }
    

      

  • 相关阅读:
    [C++]仿java.lang.String的字符串工具类[原]
    SQL基础1创建表、用户
    Linux中gdb 查看core堆栈信息
    Direct3D9基础工具类[原]
    eclipse3.4启动错误
    ndk连接第三方库
    数据库基本概念
    MySQL常见命令
    MySQL启动和停止
    MySQL配置文件
  • 原文地址:https://www.cnblogs.com/Java-125/p/8949478.html
Copyright © 2011-2022 走看看