继续以卖票为例
一、线程安全问题的解决
同步的第一种表现形式:同步代码块
思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不允许参与运算的,必须要当期线程把代码执行完毕后,其他线程才可以参与运算
在java中用同步代码块解决这个问题
同步代码块格式:
synchronized(对象)
{
需要被同步的代码部分
}
class Ticket implements Runnable { private int num = 100; Object god = new Object();//也可以自定义,建议使用已有的 public void run() { while(true) { synchronized(god) //同步 { if(num>0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName()+"..sale.."+num--); } } } } } public class Main { public static void main(String[] args) { Ticket t = new Ticket(); Thread j1 = new Thread(t); Thread j2 = new Thread(t); Thread j3 = new Thread(t); Thread j4 = new Thread(t); j1.start(); j2.start(); j3.start(); j4.start(); } }
同步的好处:解决了线程的安全问题
同步的弊端:当一个线程sleep时,释放执行权,执行权会给其他线程,然后进行判断同步锁,判断完又进不去,所以相对以前会降低效率,是在可承受的范围内
二、同步的前提
如果出现,同步后安全问题还存在,就必须了解同步的前提
前提:同步中必须有多个线程并使用同一个锁(一个线程就没必要同步,多个锁就失去同步的价值)
三、同步的第二种表现形式:同步函数
/* * 需求: * 两个人到银行存钱,每次存10,每个人都是三次 * * */ class Bank { private int sum; private Object obj = new Object(); //同步代码代码块表现形式 /*public void add(int num) { synchronized(obj) { sum += num; try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println("sum = "+sum); } }*/ //同步函数表现形式 public synchronized void add(int num) { sum += num; try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println("sum = "+sum); } } class Custom implements Runnable { private Bank bank = new Bank(); public void run() { for(int i = 0;i<3;i++) { bank.add(10); } } } class Main { public static void main(String[] args) { Custom tCustom = new Custom(); Thread j1 = new Thread(tCustom); Thread j2 = new Thread(tCustom); j1.start(); j2.start(); } }
验证同步函数的锁是什么?(了解)
class Ticket implements Runnable { private int num = 100; Object god = new Object();//也可以自定义,建议使用已有的 boolean flag = true; public void run() { if(flag==true) { while(true) { synchronized(this) //同步 { if(num>0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName()+"..obj.."+num--); } } } } else { while(true) this.show(); } } public synchronized void show() { if(num>0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName()+"..fu.."+num--); } } } class Main { public static void main(String[] args) { Ticket t = new Ticket(); Thread j1 = new Thread(t); Thread j2 = new Thread(t); j1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } t.flag = false; j2.start(); } }
同步函数的使用的锁是this
同步函数和同步代码块的区别:
同步函数的锁是固定的this,同步代码块的锁是任意的
所以同步时,建议使用同步代码块
验证静态同步函数的锁是什么?(了解)
静态同步函数的锁不是this,因为根本没有this
class Ticket implements Runnable { private static int num = 100; Object god = new Object();//也可以自定义,建议使用已有的 boolean flag = true; public void run() { if(flag==true) { while(true) { synchronized(this.getClass()) //synchronized(Ticket.class) { if(num>0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName()+"..obj.."+num--); } } } } else { while(true) this.show(); } } public static synchronized void show() { if(num>0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName()+"..fu.."+num--); } } } class Main { public static void main(String[] args) { Ticket t = new Ticket(); Thread j1 = new Thread(t); Thread j2 = new Thread(t); j1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO: handle exception } t.flag = false; j2.start(); } }
可以使用getClass方法获取,也可以使用 当前 类名.class获取
原文:http://blog.csdn.net/wjw0130/article/details/39938407