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

  

 

posted @ 2018-04-26 09:17  低调的小孩儿  阅读(221)  评论(0编辑  收藏  举报