/* 两个储户,到同一个银行存钱,每个人存了3次,一次100元。 1,描述银行。 2,描述储户任务。 分析多线程是否存在安全隐患。 1,线程任务中是否有共享的数据。 2,是否多条操作共享数据的代码。 同步函数。其实就是在函数上加上了同步关键字进行修饰。 同步表现形式有两种:1,同步代码块,2,同步函数。 同步函数使用的锁是什么呢?函数需要被对象调用,哪个对象不确定,但是都用this来表示。 同步函数使用的锁就是this。 */ class Bank { private int sum; // private Object obj = new Object(); public synchronized void add(int n) { // synchronized(obj) // { sum = sum + n; try{Thread.sleep(10);}catch(Exception e){} System.out.println("sum="+sum); // } } } class Customer implements Runnable { private Bank b = new Bank(); public void run() { for(int x=0; x<3; x++) { b.add(100); } } } class BankDemo { public static void main(String[] args) { //1,创建任务对象。 Customer c = new Customer(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
/* 验证同步函数使用的锁是this。 验证需求: 启动两个线程。 一个线程负责执行同步代码块(使用明锁)。 另一个线程使用同步函数(使用this锁)。 两个执行的任务是一样的都是卖票。如果他们没有使用相同的锁,说明他们没有同步。会出现数据错误。 怎么能让一个线程一直在同步代码块中,一个线程在同步函数呢? 可以通过切换的方式。 */ class SaleTicket implements Runnable { private int tickets = 100; //定义一个boolean标记。 boolean flag = true; Object obj = new Object(); public void run() { if(flag) while(true) { synchronized(this) { if(tickets>0) { try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。 System.out.println(Thread.currentThread().getName()+"....code....."+tickets--); } } } else while(true) sale(); } public synchronized void sale() { if(tickets>0) { try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。 System.out.println(Thread.currentThread().getName()+".....func...."+tickets--); } } } class ThisLockDemo { public static void main(String[] args) throws InterruptedException { SaleTicket t = new SaleTicket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); Thread.sleep(10); t.flag = false; t2.start(); } }
/* 如果同步函数被static修饰呢? static方法随着类加载,这时不一定有该类的对象。但是一定有一个该类的字节码文件对象。 这个对象简单的表示方式就是 类名.class Class */ class SaleTicket implements Runnable { private static int tickets = 100; //定义一个boolean标记。 boolean flag = true; Object obj = new Object(); public void run() { if(flag) while(true) { synchronized(SaleTicket.class) { if(tickets>0) { try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。 System.out.println(Thread.currentThread().getName()+"....code....."+tickets--); } } } else while(true) sale(); } public static synchronized void sale() { if(tickets>0) { try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。 System.out.println(Thread.currentThread().getName()+".....func...."+tickets--); } } } class StaticLockDemo { public static void main(String[] args) throws InterruptedException { SaleTicket t = new SaleTicket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); Thread.sleep(10); t.flag = false; t2.start(); } }
//饿汉式。相对与多线程并发,安全! class Single { private static final Single SINGLE_INTSTANCE = new Single(); private Single(){} public static Single getInstance() { return SINGLE_INTSTANCE; } } //懒汉式。延迟加载模式。 /* 在多线程并发访问时,会出现线程安全问题。 加了同步就可以解决问题。无论是同步函数,还是同步代码块都行。 但是,效率低了。 怎么解决效率低的问题。 可以通过if对单例对象的双重判断的形式。哦耶! */ class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) //-->0 s = new Single(); } } return s; } } class Demo implements Runnable { public void run() { Single.getInstance(); } } class ThreadSingleTest { public static void main(String[] args) { System.out.println("Hello World!"); } }
/* 同步函数,同步代码块。 同步代码块使用的任意的对象作为锁。 同步函数只能使用this作为锁。 如果说:一个类中只需要一个锁,这时可以考虑同步函数,使用this,写法简单。 但是,一个类中如果需要多个锁,还有多个类中使用同一个锁,这时只能使用同步代码块。 建议使用同步代码块。 */ class { public static void main(String[] args) { System.out.println("Hello World!"); } }
/* 死锁: 场景一: 同步嵌套。 */ class SaleTicket implements Runnable { private int tickets = 100; //定义一个boolean标记。 boolean flag = true; Object obj = new Object(); public void run() { if(flag) while(true) { synchronized(obj)//obj lock { sale(); } } else while(true) sale(); } public synchronized void sale()//this lock { synchronized(obj) { if(tickets>0) { try{Thread.sleep(10);}catch(InterruptedException e){}//让线程到这里稍微停一下。 System.out.println(Thread.currentThread().getName()+".....func...."+tickets--); } } } } class DeadLockDemo { public static void main(String[] args) throws InterruptedException { SaleTicket t = new SaleTicket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); Thread.sleep(10); t.flag = false; t2.start(); } }
/* 多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。 生产者,消费者。 通过同步,解决了没生产就消费的问题。 */ //描述资源。 class Res { private String name; private int count = 1; //提供了给商品赋值的方法。 public synchronized void set(String name) { this.name = name + "--" + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name); } //提供一个获取商品的方法。 public synchronized void get() { System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name); } } //生成者。 class Producer implements Runnable { private Res r; Producer(Res r) { this.r = r; } public void run() { while(true) r.set("面包"); } } //消费者 class Consumer implements Runnable { private Res r; Consumer(Res r) { this.r = r; } public void run() { while(true) r.get(); } } class ProducerConsumerDemo { public static void main(String[] args) { //1,创建资源。 Res r = new Res(); //2,创建两个任务。 Producer pro = new Producer(r); Consumer con = new Consumer(r); //3,创建线程。 Thread t1 = new Thread(pro); Thread t2 = new Thread(con); t1.start(); t2.start(); } }
//同步嵌套,死锁。 class Task implements Runnable { private boolean flag; Task(boolean flag) { this.flag = flag; } public void run() { if(flag) { while(true) synchronized(MyLock.LOCKA) { System.out.println("if.....locka"); synchronized(MyLock.LOCKB) { System.out.println("if.....lockb"); } } } else { while(true) synchronized(MyLock.LOCKB) { System.out.println("else.....lockb"); synchronized(MyLock.LOCKA) { System.out.println("else.....locka"); } } } } } class MyLock { public static final Object LOCKA = new Object(); public static final Object LOCKB = new Object(); } class DeadLockTest { public static void main(String[] args) { //创建线程任务。 Task t1 = new Task(true); Task t2 = new Task(false); new Thread(t1).start(); new Thread(t2).start(); } }
/* 多线程间的通信。多个线程都在处理同一个资源,但是处理的任务却不一样。 生产者,消费者。 通过同步,解决了没生产就消费的问题。 但是出现了连续的生产没有消费的情况,和需求生产一个,消费一个的情况不符。 使用了等待唤醒机制。 wait():该方法可以让线程处于冻结状态,并将线程临时存储到线程池中。 notify():唤醒指定线程池中的任意一个线程。 notifyAll():唤醒指定线程池中的所以线程。 这些方法必须使用在同步中,因为它们用来操作同步锁上的线程的状态的。 在使用这些方法时,必须标识它们所属于的锁。标识方式就是 锁对象.wait(); 锁对象.notify(); 锁对象.notifyAll(); 相同锁的notify(),可以获取相同锁的wait(); */ //描述资源。 class Res { private String name; private int count = 1; //定义标记。 private boolean flag; //提供了给商品赋值的方法。 public synchronized void set(String name) { if(flag)//判断标记为true,执行wait。等待。为false。就生产。 try{this.wait();}catch(InterruptedException e){} this.name = name + "--" + count; count++; System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name); //生成完毕,将标记改为true。 flag = true; //唤醒消费者。 this.notify(); } //提供一个获取商品的方法。 public synchronized void get() { if(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name); //将标记改为false。 flag = false; //唤醒生产者。 this.notify(); } } //生成者。 class Producer implements Runnable { private Res r; Producer(Res r) { this.r = r; } public void run() { while(true) r.set("面包"); } } //消费者 class Consumer implements Runnable { private Res r; Consumer(Res r) { this.r = r; } public void run() { while(true) r.get(); } } class ProducerConsumerDemo2 { public static void main(String[] args) { //1,创建资源。 Res r = new Res(); //2,创建两个任务。 Producer pro = new Producer(r); Consumer con = new Consumer(r); //3,创建线程。 Thread t1 = new Thread(pro); Thread t2 = new Thread(con); t1.start(); t2.start(); } }
/* 多生产多消费。 问题1: 重复生成,重复消费。 原因:经过复杂的(等,资格)分析,发现被唤醒的线程没有判断标记就开始工作(生成or消费)了。 导致了重复的生成和消费的发生。 解决:那就是被唤醒的线程必须判断标记。 使用while循环搞定。 问题2: 死锁了。所有的线程都处于冻结状态。 原因:本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记,又继续等待。 而导致所有的线程都等待了。 解决;希望本方如果唤醒了对方线程,就可以解决。 可以使用notifyAll()方法。 那不是全唤醒了吗?是的。既有本方,又有对方。但是本方醒后,会判断标记继续等待。 这样对方就有线程可以执行了。 已经实现了多生产多消费。 但是有些小问题,效率有点低,因为notifyAll也唤醒了本方。做了不必要的判断。 */ //描述资源。 class Res { private String name; private int count = 1; //定义标记。 private boolean flag; //提供了给商品赋值的方法。 public synchronized void set(String name)// { while(flag)//判断标记为true,执行wait。等待。为false。就生产。 try{this.wait();}catch(InterruptedException e){}//t0(等) t1(等) this.name = name + "--" + count;//面包1 ,面包2 面包3 count++;//2 3 4 System.out.println(Thread.currentThread().getName()+"...生产者....."+this.name);//t0 面包1、 t0 面包2 t1 ,面包3 //生成完毕,将标记改为true。 flag = true; //唤醒所有等待线程(包括本方线程)。 this.notifyAll(); } //提供一个获取商品的方法。 public synchronized void get()// { while(!flag) try{this.wait();}catch(InterruptedException e){}//t2(等) t3(等) System.out.println(Thread.currentThread().getName()+".......消费者....."+this.name);//t2 面包1. //将标记改为false。 flag = false; //唤醒所有等待线程(包括本方线程)。 this.notifyAll(); } } //生成者。 class Producer implements Runnable { private Res r; Producer(Res r) { this.r = r; } public void run() { while(true) r.set("面包"); } } //消费者 class Consumer implements Runnable { private Res r; Consumer(Res r) { this.r = r; } public void run() { while(true) r.get(); } } class ProducerConsumerDemo3 { public static void main(String[] args) { //1,创建资源。 Res r = new Res(); //2,创建两个任务。 Producer pro = new Producer(r); Consumer con = new Consumer(r); //3,创建线程。 Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
/* 1,搞定妖的问题。 2,name和sex是私有的。需要在Res类中对外提供访问name和sex的方法。这个可以参照生产者消费者producerconsumerdemo.java 3,实现间隔输出,使用等待唤醒机制。producerconsumerdemo2.java */ class Res { String name; String sex; } class Input implements Runnable { private Res r; Input(Res r) { this.r = r; } public void run() { int x = 0; while(true) { if(x==0) { r.name = "张三"; r.sex = "男男男男男男男"; } else { r.name = "rose"; r.sex = "women"; } x = (x+1)%2; } } } class Output implements Runnable { private Res r; Output(Res r) { this.r = r; } public void run() { while(true) { System.out.println(r.name+"...."+r.sex); } } } class Test { public static void main(String[] args) { Res r = new Res(); Input in = new Input(r); Output out = new Output(r); new Thread(in).start(); new Thread(out).start(); } }