在使用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(); 
 
 
 |   
        
 
    
    
    
     
 
 |