| 
 
| 在使用java多线程的时候如果着多个线程访问的是同一个共享数据; 
 例如售票, 多个售票点线程各自售票, 但都是从一个地方取票, 这就会造成操作系统让多个线程能够"同时"访问这个共享数据, 所谓同时并不是真正意义上的同时,而是很短间隔;
 
 只是这个线程不能一次性执行完它的线程体, 比如说它现在只拿到了共享变量的值, 但是还没有做修改,
 
 但是这时很可能会由于cpu分配给自己的时间片用完了而进入阻塞状态, 这时候另一个线程开始, 并且成功的拿到了并且修改了这个共享变量,的值;
 
 很显然他们俩拿到的是同一个值,这就是问题所在.
 于是线程同步就提出来了;
 而线程同步又是基于对象锁的;
 一般用对象锁更灵活;
 未加线程代码:
 
 package MultiThread;
 
 public class TicketSellTest{
 public static void main(String[] args) {
 System.out.println("main线程开始");
 TicketOffice tick = new TicketOffice();
 Thread s1 = new Thread(tick);
 Thread s2 = new Thread(tick);
 Thread s3 = new Thread(tick);
 s1.setName("售票点1 ");
 s2.setName("售票点2 ");
 s3.setName("售票点3 ");
 s1.start();
 s2.start();
 s3.start();
 System.out.println("main线程结束");
 }
 }
 
 class TicketOffice implements Runnable{
 // 售出的票计数器
 private int tickets = 0;
 // 不管用这个类创立多少个线程, 每个线程执行start()的时候只执行run()方法
 public void run() {
 boolean flag = true;
 while(flag) {
 flag = sell();
 }
 }
 // 售票方法
 private boolean sell() {
 boolean flag = true;
 if (tickets < 10) {
 tickets = tickets + 1;
 // 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
 System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
 }else {
 System.out.println("无票可卖!!");
 flag = false;
 }
 // 为了增大出错几率, 让线程停留一会儿
 try {
 Thread.sleep(300);
 }catch(InterruptedException e) {
 System.out.println("售卖过程中出现InterruptedException");
 e.printStackTrace();
 }
 // 返回True表示有票可卖
 return flag;
 }
 }
 // 执行结果
 // 可以看到这里就是由于不加限制访问共享变量造成的后果, 就是一个线程没有完成一个完整的操作, 就被另一个线程介入了
 main线程开始
 售票点1 卖出第 2 张票
 main线程结束
 售票点2 卖出第 2 张票
 售票点3 卖出第 3 张票
 售票点1 卖出第 4 张票
 售票点2 卖出第 6 张票
 售票点3 卖出第 6 张票
 售票点1 卖出第 7 张票
 售票点2 卖出第 8 张票
 售票点3 卖出第 9 张票
 售票点1 卖出第 10 张票
 无票可卖!!
 无票可卖!!
 无票可卖!!
 
 然后解决办法是进行线程同步,此处有两个方法:
 第一个
 
 //将sell()方法变成一个同步方法,即在方法定义的时候加入synchronized修饰符
 //如下
 private synchronized boolean sell(){}
 //这会使得此方法为同步方法,作用是每次线程执行此方法不会被打断,一定要执行完
 
 还有一个方法是将操作共享变量的那一块代码变成同步代码块,即用synchronized(this){}将操作共享变量的那段代码包围起来;
 其中括号里的参数表示获取制定对象的对象锁;
 如下:
 
 
 // 售票方法
 private  boolean sell() {
 boolean flag = true;
 // 加入同步代码块,这个this表示调用此方法的对象的引用;获取此对象的对象锁
 synchronized(this) {
 if (tickets < 10) {
 tickets = tickets + 1;
 // 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
 System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
 }else {
 System.out.println("无票可卖!!");
 flag = false;
 }
 }
 // 为了增大出错几率, 让线程停留一会儿
 try {
 Thread.sleep(300);
 }catch(InterruptedException e) {
 System.out.println("售卖过程中出现InterruptedException");
 e.printStackTrace();
 }
 // 返回True表示有票可卖
 return flag;
 }
 //这样的运行结果就不会有冲突,如下
 main线程开始
 售票点1 卖出第 1 张票
 售票点2 卖出第 2 张票
 main线程结束
 售票点3 卖出第 3 张票
 售票点1 卖出第 4 张票
 售票点2 卖出第 5 张票
 售票点3 卖出第 6 张票
 售票点1 卖出第 7 张票
 售票点2 卖出第 8 张票
 售票点3 卖出第 9 张票
 售票点1 卖出第 10 张票
 无票可卖!!
 无票可卖!!
 无票可卖!!
 
 对象锁
 
 同步机制的实现就是利用了对象锁;
 
 JVM对每一个对象都有一个对象锁; 这个对象锁代表任何时候只允许一个线程能够有访问权限;
 
 即是如果线程获取了这个对象的对象锁, 那么在它释放之前这段时间里面其他线程都不能访问;
 
 其作用和synchronized同步机制是一样的,但是这个对象锁用起来更为灵活;
 
 java提供了一种显示加锁机制, 使用java.util.concurrent.locks.Lock接口提供的lock()方法来获取锁;
 
 用unlock()方法解锁,通常使用ReentrantLock这个类来实现, 上面更改之处的示例如下:
 
 // 对象锁的实现所依赖的包
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 // 售票方法
 // 创建Lock锁实例
 private Lock lock = new ReentrantLock();
 private boolean sell() {
 boolean flag = true;
 // 获取对象锁
 lock.lock();
 if (tickets < 10) {
 tickets = tickets + 1;
 // 获取正在执行这里的线程对象的引用的名字, 即是获取正在执行这里的线程名字
 System.out.println(Thread.currentThread().getName() + "卖出第 "+tickets +" 张票");
 }else {
 System.out.println("无票可卖!!");
 flag = false;
 }
 // 释放锁
 lock.unlock();
 
 
 
 | 
 |