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