1.线程的同步(并发):
多个线程访问一个资源,那么我们就要是资源安全,方法是使线程同步;
我们使用这个关键字;
线程安全的每次只能进去一个对象,就是给他加了一个锁,这个锁只有一把钥匙,所以只能一个完成后一个在进去;
同步的时候可以选择同步块或者同步方法;(同步又叫做锁定)
package Synchronize; /** * * 线程的同步问题(线程安全不安全) * * @author Wang * */ public class Syn { public static void main(String[] args) { Web web = new Web();// 创建实体对象 Thread thr1 = new Thread(web, "路人甲");// 创建四个代理 他们来实现多线程的抢票 Thread thr2 = new Thread(web, "黄牛"); Thread thr3 = new Thread(web, "程序员"); Thread thr4 = new Thread(web, "白领"); thr1.start(); thr2.start(); thr3.start(); thr4.start(); } } class Web implements Runnable {// 线程不安全的 private int ticketNum = 10; private boolean flag = true; @Override public void run() { while (flag) { test6(); //test5(); } } public void test1() { // 线程不安全的 if (ticketNum <= 0) { flag = false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNum--); } public synchronized void test2() { // 线程安全的 这里加了一个锁 if (ticketNum <= 0) { flag = false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNum--); } //线程安全 锁定正确 public void test3(){ //a b c synchronized(this){ //锁定块 线程安全的 if(ticketNum<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--); } } //锁定范围不正确 线程不安全 public void test4(){ // c 1 synchronized(this){ //b if(ticketNum<=0){ flag=false; //跳出循环 return ; } } // b try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--); }//a -->1 //线程不安全 锁定资源不正确 public void test5(){ //a b c synchronized((Integer)ticketNum){ if(ticketNum<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--); } } //锁定的范围有误 public void test6(){ if(ticketNum<=0){ flag=false; //跳出循环 return ; } //a b c synchronized(this){ try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--); } } }
2.锁定静态的属性,我们在这里也介绍一下单例设计模式(我们在多线程的创建的时候用到了静态代理设计模式)
/**
* 单例设计模式
* 确保一个类只有一个对象
* 懒汉式 是指在声明静态属性的时候不创建对象 double checking(双重检查提高现成的效率)
* 1、构造器私有化,避免外部直接创建对象
* 2、声明一个私有的静态变量
* 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
*/
synchronized(Jvm.class){//锁住他的字节吗 让他无法创建对象 或者说锁住他的模板 但是再次可以提高效率(double checking )
package Synchronize; /** * * 单例设计模式确保一个类只有一个对象 * * @author Wang * */ /** * 单例设计模式:确保一个类只有一个对象 * @author Administrator * */ public class SynchroizeDemo02 { /** * @param args */ public static void main(String[] args) { JvmThread thread1 = new JvmThread(100); JvmThread thread2 = new JvmThread(500); thread1.start(); thread2.start(); } } class JvmThread extends Thread{ private long time; public JvmThread() { } public JvmThread(long time) { this.time =time; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance1(time)); } } /** * 单例设计模式 * 确保一个类只有一个对象 * 懒汉式 声明静态变量的时候没有创建对象 double checking 双重检查提高效率 * 1、构造器私有化,避免外部直接创建对象 * 2、声明一个私有的静态变量 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 */ class Jvm { //声明一个私有的静态变量 private static Jvm instance =null; //构造器私有化,避免外部直接创建对象 private Jvm(){ } //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 public static Jvm getInstance(long time){ //线程不安全的创建对像的地址都不一样 if(instance == null){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 public static Jvm getInstance1(long time){ synchronized(Jvm.class){//锁住他的字节吗 让他无法创建对象 或者说锁住他的模板 但是再次可以提高效率 if(instance == null){//存在对象也需要等待 try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } } return instance; } //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 public static Jvm getInstance2(long time){ // c d e -->效率 提供 已经存在对象的访问效率 if(null==instance){ //这里我们用double-checking 的方法来提高现成的效率 // a b synchronized(Jvm.class){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } } }//a return instance; } }
单例设计方法的懒汉与饿汉式
package Synchronize; /** * 单例创建的方式 * 1、懒汉式 * 1)、构造器私有化 * 2)、声明私有的静态属性 * 3)、对外提供访问属性的静态方法,确保该对象存在 * * @author Administrator * */ public class Demo03 { private static Demo03 instance; private Demo03(){ } public static Demo03 getInstance (){ if(instance == null){ //提供效率 synchronized(Demo03.class){ if(null==instance){ //安全 instance =new Demo03(); } } } return instance; } } /** * 饿汉式 1)、构造器私有化 * 2)、声明私有的静态属性,同时创建该对象 * 3)、对外提供访问属性的静态方法 * @author Administrator * */ class MyJvm2 { private static MyJvm2 instance =new MyJvm2(); private MyJvm2(){ } public static MyJvm2 getInstance (){//饿汉式是不需要同步的也不需要判断对象是否存在因为 类会在时候的加载他的属性 那么就会在加载第一句的时候创建对象 return instance; } } /** * 类在使用的时候加载 ,延缓加载时间 */ class MyJvm3 { private static class JVMholder{//内部类 这里就会提高效率 只有getInstance ()被调用的时候他才会被加载 跟MyJvm03加载就没什么关系了 private static MyJvm3 instance =new MyJvm3(); } private MyJvm3(){ } public static MyJvm3 getInstance (){ return JVMholder.instance; } }
注意:懒汉式与饿汉式都有提高效率的方法
3.过多的同步方法可能会造成死锁;
package Synchronize; /** * 过多的同步方法可能造成死锁 * @author Wang * */ public class Demo04 { /** * @param args */ public static void main(String[] args) { Object g =new Object(); Object m = new Object(); Test t1 =new Test(g,m); Test2 t2 = new Test2(g,m); Thread proxy = new Thread(t1); Thread proxy2 = new Thread(t2); proxy.start(); proxy2.start(); } } class Test implements Runnable{ Object goods ; Object money ; public Test(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized(goods){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(money){ } } System.out.println("一手给钱"); } } class Test2 implements Runnable{ Object goods ; Object money ; public Test2(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized(money){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(goods){ } } System.out.println("一手给货"); } }
4.解决死锁的办法 生产者消费者模式:(注意这个不是设计模式)
设计模式:是将类于类的关系;
package lock; /** * 一个场景,共同的资源 生产者消费者模式 信号灯法 wait() :等待,释放锁 sleep 不释放锁 notify()/notifyAll():唤醒 与 * synchronized * * @author Administrator * */ public class Moive { private String pic; // 信号灯 // flag -->T 生产生产,消费者等待 ,生产完成后通知消费 // flag -->F 消费者消费 生产者等待, 消费完成后通知生产 private boolean flag = true; public synchronized void play(String pic) { if (!flag) { // 生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 开始生产 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产了:" + pic); // 生产完毕 this.pic = pic; // 通知消费 this.notify(); // 生产者停下 this.flag = false; } public synchronized void watch() { if (flag) { // 消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 开始消费 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("消费了" + pic); // 消费完毕 // 通知生产 this.notifyAll(); // 消费停止 this.flag = true; } }
package lock; public class Player implements Runnable { private Moive m; Player(Moive m) { this.m = m; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i % 2 == 0) { m.play("缝纫机乐队"); } else { m.play("最好的我们"); } } } }
package lock; public class Watcher implements Runnable { private Moive m; Watcher(Moive m) { this.m = m; } @Override public void run() { for (int i = 0; i < 20; i++) { m.watch(); } } }
package lock; public class Apply { public static void main(String[] args) { Moive m = new Moive(); Player p = new Player(m); Watcher w = new Watcher(m); new Thread(p).start(); new Thread(w).start(); } }
5.任务的调度
package task; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * * Timer() * schedule(TimerTask task, Date time) * schedule(TimerTask task, Date firstTime, long period) * 可以自学一下 quartz(框架) 关于任务调度的 * * @author Wang * */ public class Time { public static void main(String[] args) { Timer timer =new Timer(); timer.schedule(new TimerTask(){ //使用匿名内部类 创建一个对象 因为继承了Runnable所以还要重写run() 方法 @Override public void run() { System.out.println("so easy...."); }}, new Date(System.currentTimeMillis()+1000), 2000);//1s之后开始执行 每间隔两秒执行一次 } }
注意:
wait() :等待,释放锁; sleep 不释放锁; notify()/notifyAll(): 唤醒 他们都必须要与 synchronized连用才有效果;
线程安全的效率就会低;