一.线程不安全示例:
代码1:
结果出现数据错误:
[img]http://dl.iteye.com/upload/picture/pic/132656/5ae144ca-75cb-3b4e-824e-2b33d65b6c57.png[/img]
错误原因:
当多条语句在操作同一个线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
1.将TicketDemo2改为:
代码2:
结果没有出现数据错误:
[img]http://dl.iteye.com/upload/picture/pic/132658/fbbab33b-d135-3903-81b1-37877bf1643e.png[/img]
原因:代码1中3个线程对同一个ticket对象操作(多线程),ticket是共享数据,多线程环境中容易发生安全问题;
代码2中是3线程不是对同一个ticket对象操作,线程t1、t2、t3分别对不同的ticket对象操作,3个ticket对象不是指向同一个内存地址,所以t1、t2、t3不存在共享数据,所以在此例中不会出现线程安全问题。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
同步代码块:
解决代码1中的不安全问题:
2.同步函数
通俗解释:火车上的卫生间---经典
同步可以解决线程的安全问题;
同步的前提:
a.必须要有两个或两个以上的线程。
b.必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程运行。
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
有的需要同步,有的不需要同步,同步范围过大导致程序性能差;
列子2: 存钱——————————————————————————————————
二.线程不安全 :
代码3:
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132660/2f2c6710-c4bd-3c73-96a3-667ce024d38d.png[/img]
解决代码3中的线程不安全问题:
代码4:同步共享数据
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132662/7acfb964-395f-378f-8d26-6c1ff32d9816.png[/img]
代码5:同步this
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132664/e3c4cb95-5745-331e-b8f8-3a2774c15e3f.png[/img]
代码6:同步函数
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132666/5a381790-a639-3442-8a79-0a76ba10a4d5.png[/img]
证实同步函数使用的锁是 this
代码7:
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132668/821c3829-a3f0-3645-b4cf-256e567c89ff.png[/img]
分析原因:加了同步还出现问题,就是同步前提出问题了:
同步的前提:
1.必须要有两个或两个以上的线程。(满足)
2.必须是多个线程使用同一个锁。(不是)
一个是obj锁,一个是this锁。
验证下,将obj改为this测试:
[img]http://dl.iteye.com/upload/picture/pic/132670/2d1c8869-a1d2-31e3-a310-c294fffe0783.png[/img]
[img]http://dl.iteye.com/upload/picture/pic/132672/0f23206c-9500-307a-ae26-a48880a6d29a.png[/img]
这次就没问题了。
3.如果同步函数被静态修饰后,使用的锁是什么呢?
代码8:
运行结果:线程不安全
[img]http://dl.iteye.com/upload/picture/pic/132674/776bd915-0a0f-3bf8-9c62-0521766c1bac.png[/img]
[size=medium][color=red]总结:静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。
synchronized(this){ }等价于publicsynchronized void method(){.....}
[/color][/size]
代码1:
class Ticket implements Runnable
{
public int tick = 100;
Object obj = new Object();
boolean flag = true;
@Override
public void run() {
while (true) {
if(tick>0)
{
try { Thread.sleep(10); }
catch (InterruptedException e)
{ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
}else {
break;
}
}
}
}
class TicketDemo2{
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket,"张三");
Thread t2 = new Thread(ticket,"李四");
Thread t3 = new Thread(ticket,"王五");
t1.start();
t2.start();
t3.start();
}
}
结果出现数据错误:
[img]http://dl.iteye.com/upload/picture/pic/132656/5ae144ca-75cb-3b4e-824e-2b33d65b6c57.png[/img]
错误原因:
当多条语句在操作同一个线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
1.将TicketDemo2改为:
代码2:
class TicketDemo2{
public static void main(String[] args) {
Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
Thread t1 = new Thread(ticket1,"张三");
Thread t2 = new Thread(ticket2,"李四");
Thread t3 = new Thread(ticket3,"王五");
t1.start();
t2.start();
t3.start();
}
}
结果没有出现数据错误:
[img]http://dl.iteye.com/upload/picture/pic/132658/fbbab33b-d135-3903-81b1-37877bf1643e.png[/img]
原因:代码1中3个线程对同一个ticket对象操作(多线程),ticket是共享数据,多线程环境中容易发生安全问题;
代码2中是3线程不是对同一个ticket对象操作,线程t1、t2、t3分别对不同的ticket对象操作,3个ticket对象不是指向同一个内存地址,所以t1、t2、t3不存在共享数据,所以在此例中不会出现线程安全问题。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
同步代码块:
synchronized (obj) {
}
解决代码1中的不安全问题:
class Ticket implements Runnable
{
public int tick = 100;
Object obj = new Object();
boolean flag = true;
@Override
public void run() {
while (true) {
synchronized (obj) {
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
}else {
break;
}
}
}
}
}
class TicketDemo2{
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
}
}
2.同步函数
/**
同步函数使用的this函数
*/
class Ticket implements Runnable
{
public int tick = 100;
Object obj = new Object();
boolean flag = true;
@Override
public synchronized void run() {
while (true) {
show();
}
}
public synchronized void show()
{
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
}
}
}
通俗解释:火车上的卫生间---经典
同步可以解决线程的安全问题;
同步的前提:
a.必须要有两个或两个以上的线程。
b.必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程运行。
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
有的需要同步,有的不需要同步,同步范围过大导致程序性能差;
列子2: 存钱——————————————————————————————————
二.线程不安全 :
代码3:
package ticket03;
/**
* 需求:
* 银行有一个金库。
* 有两个储户分别寸300元,每次寸100,寸3次。
*
* */
class Bank
{
private int sum;
public void add(int n) {
sum = sum + n;
System.out.println("sum="+sum);
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
@Override
public void run() {
for (int i=0;i<3;i++) {
b.add(100);
}
}
}
class BankDemo{
public static void main(String[] args) {
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132660/2f2c6710-c4bd-3c73-96a3-667ce024d38d.png[/img]
解决代码3中的线程不安全问题:
代码4:同步共享数据
class Bank
{
private int sum;
Object obj = new Object();
public void add(int n) {
synchronized(obj)
{
sum = sum + n;
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println("sum="+sum);
}
}
}
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132662/7acfb964-395f-378f-8d26-6c1ff32d9816.png[/img]
代码5:同步this
class Bank
{
private int sum;
//Object obj = new Object();
public void add(int n) {
synchronized(this)
{
sum = sum + n;
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"---sum="+sum);
}
}
}
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132664/e3c4cb95-5745-331e-b8f8-3a2774c15e3f.png[/img]
代码6:同步函数
class Bank
{
private int sum;
public synchronized void add(int n) {
sum = sum + n;
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"---sum="+sum);
}
}
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132666/5a381790-a639-3442-8a79-0a76ba10a4d5.png[/img]
证实同步函数使用的锁是 this
代码7:
package ticket02;
/**
* 同步函数用的是那一个锁呢?
* 函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
* 所以同步函数使用的锁是 this。
*
* 通过改程序进行验证。
*
* 使用两个线程来买票。
* 一个线程在同步代码块中,
* 一个线程在同步函数中。
*
* 都在执行买票动作
* */
class Ticket implements Runnable
{
public int tick = 100;
Object obj = new Object();
boolean flag = true;
@Override
public void run() {
if(flag)
{
while (true) {
synchronized (obj) {
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
}else {
break;
}
}
}
}else {
while (true)
show();
}
}
public synchronized void show()
{
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...show...: "+tick--);
}
}
}
class TicketDemo2{
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
ticket.flag=false;
t2.start();
}
}
运行结果:
[img]http://dl.iteye.com/upload/picture/pic/132668/821c3829-a3f0-3645-b4cf-256e567c89ff.png[/img]
分析原因:加了同步还出现问题,就是同步前提出问题了:
同步的前提:
1.必须要有两个或两个以上的线程。(满足)
2.必须是多个线程使用同一个锁。(不是)
一个是obj锁,一个是this锁。
验证下,将obj改为this测试:
[img]http://dl.iteye.com/upload/picture/pic/132670/2d1c8869-a1d2-31e3-a310-c294fffe0783.png[/img]
[img]http://dl.iteye.com/upload/picture/pic/132672/0f23206c-9500-307a-ae26-a48880a6d29a.png[/img]
这次就没问题了。
3.如果同步函数被静态修饰后,使用的锁是什么呢?
代码8:
package ticket02;
/**
* 如果同步函数被静态修饰后,使用的锁是什么呢?
* 通过验证,发现不在是this。因为静态方法中也不可以定义this。
*
* 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
* 类名.class,该对象的类型是 class
*
* 静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。
* */
class Ticket implements Runnable
{
public static int tick = 100;
Object obj = new Object();
boolean flag = true;
@Override
public void run() {
if(flag)
{
while (true) {
synchronized (this) {
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
}else {
break;
}
}
}
}else {
while (true)
show();
}
}
public static synchronized void show()
{
if(tick>0)
{
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"...show...: "+tick--);
}
}
}
class TicketDemo2{
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
ticket.flag=false;
t2.start();
}
}
运行结果:线程不安全
[img]http://dl.iteye.com/upload/picture/pic/132674/776bd915-0a0f-3bf8-9c62-0521766c1bac.png[/img]
[size=medium][color=red]总结:静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。
synchronized(this){ }等价于publicsynchronized void method(){.....}
[/color][/size]