public class Tickets implements Runnable { private int num = 100; //(1) @Override public void run() { // (2) // 死循环,一直处于能够售票状态 while(true) { // (3) if(num>0) { // (4) System.out.println(Thread.currentThread().getName()+" 第 "+ num-- + " 张票售出"); //(5) } } } } public class TicketsDemo { public static void main(String[] args) { //(6) Tickets t = new Tickets(); // (7) new Thread(t).start(); // (8) new Thread(t).start(); // (9) new Thread(t).start(); // (10) } }
若每一个线程中对全局变量、静态变量只有读操做,而无写操做,通常来讲,这个全局变量是线程安全的;如有多个线程同时执行写操做,通常都须要考虑线程同步,不然的话就可能影响线程安全。java
synchronized (锁对象) { 可能会产生线程安全问题的代码 }
同步代码块中的锁对象能够是任意的对象;但多个线程时,要使用同一个锁对象才可以保证线程安全。设计模式
使用同步代码块,对电影院卖票案例中Ticket类进行以下代码修改:安全
/* 经过线程休眠,出现安全问题 解决安全问题,Java程序,提供同步技术 公式: syncronized (任意对象){ 线程要操做的共享数据 } */ public class Tickets implements Runnable { // 定义出售的票数 private int num = 100; Object obj = new Object(); // 建立对象,用于同步 @Override public void run() { // 死循环,一直处于能够售票状态 while(true) { // 线程共享数据,保证安全,加入同步代码块 synchronized (obj) { if(num>0) { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" 第 "+ num-- + " 张票售出"); } } } } }
public synchronized void method(){ 可能会产生线程安全问题的代码 }
/* 采用同步方法的形式解决线程安全问题 好处:代码量少,简洁 作法:将线程共享数据和同步抽取到方法中 */ public class Tickets implements Runnable { private int num = 100; @Override public void run() { // 死循环,一直处于能够售票状态 while(true) { payTicket(); } } public synchronized void payTicket() { if(num>0) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" 第 "+ num-- + " 张票售出"); } } }
public static synchronized void method(){ // 可能会产生线程安全问题的代码 }
ReentrantLock
void lock()
:得到锁。void unlock()
:释放锁。import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* 使用JDK1.5+的接口Locl,替换同步代码块,实现线程安全 具体使用: Lock接口中的方法: lock(); // 获取锁 unlock(); // 释放锁 实现类:ReentrantLock */ public class Tickets implements Runnable { // 存储票数 private static int num = 100; //在类的成员位置,建立Lock接口的实现类对象 private Lock lock = new ReentrantLock(); @Override public void run() { // 死循环,一直处于能够售票状态 while(true) { // 调用Lock接口中的方法,获取锁 lock.lock(); try { if(num>0) { Thread.sleep(200); System.out.println(Thread.currentThread().getName()+" 第 "+ num-- + " 张票售出"); } }catch (InterruptedException e) { e.printStackTrace(); }finally { // 释放锁,调用unlock方法 lock.unlock(); } } } }
锁的嵌套状况以下:多线程
synchronzied(A锁){ synchronized(B锁){ } } synchronzied(B锁){ synchronized(A锁){ } }
两个线程每一个得到一个锁,且都须要对方的锁才能继续执行,所以都会一直除以阻塞状态,没法恢复,出现死锁。ide
咱们进行下死锁状况的代码演示:测试
// 定义锁对象类 /* 不容许任何类建立该对象 只能经过类名调用静态成员调用,不容许new 保证了锁的惟一性 */ public class LockA { private LockA() {} public final static LockA locka = new LockA(); } public class LockB { private LockB() {} public final static LockB lockb = new LockB(); } // 线程任务类 public class DeadLock implements Runnable{ private int i = 0; @Override public void run() { while(true) { if(i%2==0) { // 先进入A同步,再进入B同步 synchronized (LockA.locka) { System.out.println(i+" --> if...locka"); synchronized (LockB.lockb) { System.out.println(i+" --> if...lockb"); } } }else { // 先进入B同步,再进入B同步 synchronized (LockB.lockb) { System.out.println(i+" --> else...lockb"); synchronized (LockA.locka) { System.out.println(i+" --> else...locka"); } } } i++; } } } // 测试类 public class DeadLockDemo { public static void main(String[] args) { DeadLock deadLock = new DeadLock(); new Thread(deadLock).start(); new Thread(deadLock).start(); } } // 运行结果: 0 --> if...locka 0 --> if...lockb 1 --> else...lockb 1 --> if...locka
Resource.javathis
/* 定义资源类,有2个成员变量: name,sex 同时有两个线程,对资源中的变量操做 1个对name,sex赋值 1个对name,sex作变量的输出打印 */ public class Resource { public String name; public String sex; }
Input.java线程
/* 输入线程: 对资源对象Resource中的成员变量赋值 要求: 一次赋值:张三,男 另外一次:李四,女 */ public class Input implements Runnable { private Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int i = 0; while(true) { if(i%2==0) { r.name = "张三"; r.sex = "男"; }else { r.name = "lisi"; r.sex = "nv"; } i++; } } }
Output.java设计
/* 输出线程:对资源对象Resource中的成员变量输出值 */ public class Output implements Runnable { private Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while(true) { System.out.println("姓名:"+r.name + ", 性别:"+r.sex); } } }
ThreadDemo.javacode
/* 开启输入线程和输出线程,实现赋值和打印 */ public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); //共享数据 Input in = new Input(r); Output out = new Output(r); new Thread(in).start(); new Thread(out).start(); } }
此时会出现问题:打印出的结果并非想要的结果
姓名:lisi, 性别:nv 姓名:张三, 性别:nv 姓名:lisi, 性别:男 姓名:lisi, 性别:nv 姓名:lisi, 性别:nv 姓名:张三, 性别:男
Input.java修改
/* 输入线程: 对资源对象Resource中的成员变量赋值 要求: 一次赋值:张三,男 另外一次:李四,女 */ public class Input implements Runnable { private Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int i = 0; while(true) { synchronized (r) { if(i%2==0) { r.name = "张三"; r.sex = "男"; }else { r.name = "lisi"; r.sex = "nv"; } } i++; } } }
Input.java修改
/* 输入线程: 对资源对象Resource中的成员变量赋值 要求: 一次赋值:张三,男 另外一次:李四,女 */ public class Input implements Runnable { private Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int i = 0; while(true) { synchronized (r) { if(i%2==0) { r.name = "张三"; r.sex = "男"; }else { r.name = "lisi"; r.sex = "nv"; } } i++; } } }
Output.java修改:
/* 输出线程:对资源对象Resource中的成员变量输出值 */ public class Output implements Runnable { private Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while(true) { synchronized (r) { System.out.println("姓名:"+r.name + ", 性别:"+r.sex); } } } }
此时还有问题:输出没有交替进行
姓名:张三, 性别:男 姓名:张三, 性别:男 姓名:张三, 性别:男 姓名:lisi, 性别:nv 姓名:lisi, 性别:nv 姓名:lisi, 性别:nv 姓名:lisi, 性别:nv
Resource.java修改
/* 定义资源类,有2个成员变量: name,sex 同时有两个线程,对资源中的变量操做 1个对name,sex赋值 1个对name,sex作变量的输出打印 */ public class Resource { public String name; public String sex; public boolean flag = false; }
Input.java修改
/* 输入线程: 对资源对象Resource中的成员变量赋值 要求: 一次赋值:张三,男 另外一次:李四,女 */ public class Input implements Runnable { private Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int i = 0; while(true) { synchronized (r) { if(r.flag) { // 标记是true,等待 try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if(i%2==0) { r.name = "张三"; r.sex = "男"; }else { r.name = "lisi"; r.sex = "nv"; } // 标记改成true,将对方线程唤醒 r.flag = true; r.notify(); } i++; } } }
Output.java修改
/* 输出线程:对资源对象Resource中的成员变量输出值 */ public class Output implements Runnable { private Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while(true) { synchronized (r) { if(!r.flag) { // 判断标记,false,等待 try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("姓名:"+r.name + ", 性别:"+r.sex); r.flag = false; r.notify(); } } } }
同步代码块
synchronized (锁对象){ 可能产生线程安全问题的代码 }
同步方法
public synchronized void method() 可能产生线程安全问题的代码 } // 同步方法中的锁对象是 this
静态同步方法
public synchronized void method() 可能产生线程安全问题的代码 } // 静态同步方法中的锁对象是 类名.class