synchronized简介
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法,有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。二、当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。三、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的除synchronized(this)同步代码块以外的部分。四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。五、以上规则对其它对象锁同样适用
源起:卖票的故事
/* 1.分别用继承Thread方式和实现Runnable方式,看看能不能共享类的实例变量? 2.这个例子用实现接口的方式 */ package me.demothread4; //1.实现线程Runnable接口,复写run() class Ticket_cn implements Runnable{ private int ticket=20; public void run(){ while (true){ if(ticket>0) System.out.println(Thread.currentThread().getName()+" 正在买票,剩余票为: "+ticket--); } } } public class DemoThread4{ public static void main(String[] args){ //2.创建Ticket_cn对吸纳个 Ticket_cn com_12306= new Ticket_cn(); //3.创建线程Thread对象, Thread windows_A=new Thread(com_12306,"A窗口"); Thread windows_B=new Thread(com_12306,"B窗口"); Thread windows_C=new Thread(com_12306,"C窗口"); Thread windows_D=new Thread(com_12306,"D窗口"); Thread windows_E=new Thread(com_12306,"E窗口"); //Thread多个构造函数,这个可以制定线程名称 //开启线程 windows_A.start(); windows_B.start(); windows_C.start(); windows_D.start(); windows_E.start(); /*输出结果: A窗口 正在买票,剩余票为: 20 D窗口 正在买票,剩余票为: 16 E窗口 正在买票,剩余票为: 17 C窗口 正在买票,剩余票为: 18 B窗口 正在买票,剩余票为: 19 C窗口 正在买票,剩余票为: 12 E窗口 正在买票,剩余票为: 13 D窗口 正在买票,剩余票为: 14 A窗口 正在买票,剩余票为: 15 D窗口 正在买票,剩余票为: 8 E窗口 正在买票,剩余票为: 9 C窗口 正在买票,剩余票为: 10 B窗口 正在买票,剩余票为: 11 C窗口 正在买票,剩余票为: 4 E窗口 正在买票,剩余票为: 5 D窗口 正在买票,剩余票为: 6 A窗口 正在买票,剩余票为: 7 E窗口 正在买票,剩余票为: 1 C窗口 正在买票,剩余票为: 2 B窗口 正在买票,剩余票为: 3 *///所有窗口都卖了看似完美的。 } }
隐患:sleep()引起的错误
卖票当然剩余票最多只能为1咯,当我们在卖票前让线程sleep会发生什么情况呢?
/* 1.运用sleep()函数,证明存在安全问题 */ package me.demothread5; //1.实现线程Runnable接口,复写run() class Ticket_cn implements Runnable{ private int ticket=30; public void run(){ while (true){ if(ticket>0){ //在这个地方增加sleep()方法,然后看看结果 try{ Thread.sleep(10); }catch(Exception ea){ } //sleep是类方法,直接类明调用,会抛出异常需要try~ System.out.println(Thread.currentThread().getName()+" 正在买票,剩余票为: "+ticket--); } } } } public class DemoThread5{ public static void main(String[] args){ //2.创建Ticket_cn对吸纳个 Ticket_cn com_12306= new Ticket_cn(); //3.创建线程Thread对象, Thread windows_A=new Thread(com_12306,"A窗口"); Thread windows_B=new Thread(com_12306,"B窗口"); Thread windows_C=new Thread(com_12306,"C窗口"); Thread windows_D=new Thread(com_12306,"D窗口"); Thread windows_E=new Thread(com_12306,"E窗口"); //Thread多个构造函数,这个可以制定线程名称 //开启线程 windows_A.start(); windows_B.start(); windows_C.start(); windows_D.start(); windows_E.start(); /*输出: A窗口 正在买票,剩余票为: 29 D窗口 正在买票,剩余票为: 28 B窗口 正在买票,剩余票为: 30 E窗口 正在买票,剩余票为: 30 C窗口 正在买票,剩余票为: 29 A窗口 正在买票,剩余票为: 27 D窗口 正在买票,剩余票为: 26 E窗口 正在买票,剩余票为: 24 B窗口 正在买票,剩余票为: 25 C窗口 正在买票,剩余票为: 23 A窗口 正在买票,剩余票为: 22 D窗口 正在买票,剩余票为: 21 E窗口 正在买票,剩余票为: 20 B窗口 正在买票,剩余票为: 19 C窗口 正在买票,剩余票为: 18 A窗口 正在买票,剩余票为: 17 D窗口 正在买票,剩余票为: 16 E窗口 正在买票,剩余票为: 15 B窗口 正在买票,剩余票为: 14 C窗口 正在买票,剩余票为: 13 A窗口 正在买票,剩余票为: 12 D窗口 正在买票,剩余票为: 11 B窗口 正在买票,剩余票为: 9 E窗口 正在买票,剩余票为: 10 C窗口 正在买票,剩余票为: 8 A窗口 正在买票,剩余票为: 7 D窗口 正在买票,剩余票为: 6 B窗口 正在买票,剩余票为: 5 E窗口 正在买票,剩余票为: 4 C窗口 正在买票,剩余票为: 3 A窗口 正在买票,剩余票为: 2 D窗口 正在买票,剩余票为: 1 B窗口 正在买票,剩余票为: 0 E窗口 正在买票,剩余票为: -1 A窗口 正在买票,剩余票为: -3 C窗口 正在买票,剩余票为: -2 *///出大事儿了~~居然有0 -1 -2 -3 } }
修正:用synchronized修正错误
/* 1.运用sleep()函数,证明存在安全问题 2.输出结果,出现原因?{ 1.当多条语句在操作共享数据时候,例如共同使用ticket时候, 2.当线程A在执行 (ticket--)时候,没有执行结束时候,线程B进来也执行。 3.这时候就出现了数据错误,所以会有0 -1 还有两次10~ } 3.解决方式{ 1.多条操作共享数据时候,只让一个线程执行完。执行过程不允许其他线程进来 2.Java中可以使用 [同步代码块,同步函数] 实现。 } */ package me.demothread5; //1.实现线程Runnable接口,复写run() class Ticket_cn implements Runnable{ private int ticket=30; public void run(){ while (true){ //这里加上同步函数 synchronized(this){ if(ticket>0){ //在这个地方增加sleep()方法,然后看看结果 try{ Thread.sleep(10); }catch(Exception ea){ } //sleep是类方法,直接类明调用,会抛出异常需要try~ System.out.println(Thread.currentThread().getName()+" 正在买票,剩余票为: "+ticket--); } } } } } public class DemoThread5{ public static void main(String[] args){ //2.创建Ticket_cn对吸纳个 Ticket_cn com_12306= new Ticket_cn(); //3.创建线程Thread对象, Thread windows_A=new Thread(com_12306,"A窗口"); Thread windows_B=new Thread(com_12306,"B窗口"); Thread windows_C=new Thread(com_12306,"C窗口"); Thread windows_D=new Thread(com_12306,"D窗口"); Thread windows_E=new Thread(com_12306,"E窗口"); //Thread多个构造函数,这个可以制定线程名称 //开启线程 windows_A.start(); windows_B.start(); windows_C.start(); windows_D.start(); windows_E.start(); /*输出: A窗口 正在买票,剩余票为: 30 A窗口 正在买票,剩余票为: 29 A窗口 正在买票,剩余票为: 28 A窗口 正在买票,剩余票为: 27 A窗口 正在买票,剩余票为: 26 A窗口 正在买票,剩余票为: 25 A窗口 正在买票,剩余票为: 24 A窗口 正在买票,剩余票为: 23 A窗口 正在买票,剩余票为: 22 A窗口 正在买票,剩余票为: 21 A窗口 正在买票,剩余票为: 20 A窗口 正在买票,剩余票为: 19 A窗口 正在买票,剩余票为: 18 A窗口 正在买票,剩余票为: 17 A窗口 正在买票,剩余票为: 16 A窗口 正在买票,剩余票为: 15 A窗口 正在买票,剩余票为: 14 A窗口 正在买票,剩余票为: 13 A窗口 正在买票,剩余票为: 12 A窗口 正在买票,剩余票为: 11 A窗口 正在买票,剩余票为: 10 A窗口 正在买票,剩余票为: 9 A窗口 正在买票,剩余票为: 8 A窗口 正在买票,剩余票为: 7 A窗口 正在买票,剩余票为: 6 A窗口 正在买票,剩余票为: 5 A窗口 正在买票,剩余票为: 4 A窗口 正在买票,剩余票为: 3 A窗口 正在买票,剩余票为: 2 A窗口 正在买票,剩余票为: 1 *///嗯,是没问题了,但是其他线程呢?因为当A有执行权时候他速度快,在执行权期间就卖完了所有 //当我们把票数增大时候就可一看到其他线程也参与进来了 } }
/* 1.增加票数,看看其他线程是否参与执行 */ package me.demothread5; //1.实现线程Runnable接口,复写run() class Ticket_cn implements Runnable{ private int ticket=1000; public void run(){ while (true){ //这里加上同步函数 synchronized(this){ if(ticket>0){ //在这个地方增加sleep()方法,然后看看结果 try{ Thread.sleep(10); }catch(Exception ea){ } //sleep是类方法,直接类明调用,会抛出异常需要try~ System.out.println(Thread.currentThread().getName()+" 正在买票,剩余票为: "+ticket--); } } } } } public class DemoThread5{ public static void main(String[] args){ //2.创建Ticket_cn对吸纳个 Ticket_cn com_12306= new Ticket_cn(); //3.创建线程Thread对象, Thread windows_A=new Thread(com_12306,"A窗口"); Thread windows_B=new Thread(com_12306,"B窗口"); Thread windows_C=new Thread(com_12306,"C窗口"); Thread windows_D=new Thread(com_12306,"D窗口"); Thread windows_E=new Thread(com_12306,"E窗口"); //Thread多个构造函数,这个可以制定线程名称 //开启线程 windows_A.start(); windows_B.start(); windows_C.start(); windows_D.start(); windows_E.start(); }
输出结果:
作者:YangGan
出处:http://blog.csdn.net/incyanggan
本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名Yanggan(包含链接).