黑马程序员-多线程技术

------- android培训java培训、期待与您交流! ----------

线程简介

   进程:是一个正在执行中的程序

          每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

   线程:就是进程中一个独立的控制单元

          线程在控制着进程的执行。

   一个进程中至少要有一个线程

  开启多个线程是为了同时运行多部分代码

  每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务

  多线程的好处:解决了多部分同时运行的问题

  多线程的弊端:线程太多回到的效率降低

  其实应用程序的执行都是cpu在做着快速的切换完成的 ,这个切换是随机的

我们可以形象把多线程的运行行为在互相抢夺cpu执行权。

这就是多线程的随机性。

 jvm启动时就启动了多个线程,至少有两个线程可以分析出来

 1,执行main函数的线程

    该线程的任务代码都定义在main函数中

 2,负责垃圾回收的线程

 

如何创建一个线程

   方式一 继承thread类

     步骤:

      1,定义类继承Thread.

      2,复写Thread类中的run方法。

          目的:将自定义代码存储在run方法。让线程运行。

      3,调用线程的start方法。该方法有两个作用。

                 启动线程,调用run方法。

  为什么要覆盖run方法呢?

      Thread类用于描述线程。

          该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。

         run方法是用于存储线程要运行的代码。

public class Demo {  public static void main(String[] args) {   /*    * 创建线程的目的就是为了开启一条执行路径,去运行的代码和其他代码实现同时运行    *    * 而运行的代码就是这个执行路径的任务    *    * jvm创建的主线程的任务都定义在了主函数中。    *    * 而自定义的线程它的任务在哪呢? Thread类用于描述线程,线程是需要任务的,所以Thread类也对任务的描述    * 这个任务就是通过Thread类的run方法来体现的, 也就是说,run方法封装自定义线程运行任务的函数    *    * run方法中定义就是线程要运行的任务代码    *    * 开启线程就是运行指定代码,所以只有继承Thread类,并复写run方法。 将运行的代码定义在run方法中即可    */   Demo4 d4 = new Demo4("周珂珂");   Demo4 d5 = new Demo4("张三");   d4.start();// 开启线程调用run方法   d5.start();   System.out.println("结束了这个线程。。" + Thread.currentThread().getName());  } } /**  * 继承方式  * @author Administrator  *  */ class Demo4 extends Thread {  private String name;

 Demo4(String name) {   super(name);//给定义的线程命名  }     //重写run方法  public void run() {   //循环测试   for (int x = 0; x < 10; x++) {    System.out.println(name + ".." + x + "..."      + Thread.currentThread().getName());   }  } }

线程运行状态

  新建状态(New):新创建了一个线程对象。 

  就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。 

  没有执行资格的情况下是冻结状态。sleep(), 时间到  wait(),notify()
  有执行资格的状态叫做临时状态。

   既有资格又有执行权运行状态。

   死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程对象以及名称

   原来线程都有自己默认的名称。

   Thread-编号  该编号从0开始。

   Thread 对象的setName() getName();方法

   线程初始化名称:构造方法 super(name);

     Thread.currentThread();返回对当前正在执行的线程对象的引用。

 创建线程方法之二  实现Runnable接口

     

/*
 * 创建线程的第二种方式:实现Runnable接口
 * 1,定义类实现Runnable接口
 * 2,覆盖接口中的run方法,将线程的任务代码封装到run方法中
 * 3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递
 *  为什么呢?因为线程的任务都封装在Runnable接口子类对象的run方法中。
 *  所以要线程对象的start方法开启线程
 *  4,调用线程对象的start方法开启线程
 * 
 *  实现Runnable接口的好处:
 *  1,将线程的任务从线程的子类中分离出来,进行单独的封装
 *      按照面向对象的思想将任务封装成对象
 *  2,避免了java单继承的局限性
 *    所以创建线程的第二种方式较为常用
 */
public class Test9 {
	public static void main(String[] args) {
		TextThread t = new TextThread();
		Thread t1 = new Thread(t);//线程1
		Thread t2 = new Thread(t);//线程2
		t1.start();
		t2.start();
	}
}
/**
 * 继承方式,实现Runnable接口
 * @author Administrator
 *
 */
class TextThread implements Runnable {
 //重写run方法
	@Override
	public void run() {
		// TODO Auto-generated method stub
		show();
	}
    //测试方法
	public void show() {
		for (int i = 0; i < 20; i++) {
			System.out.println(Thread.currentThread().getName() + "..." + i);

		}
	}

}

 

实现方式好处:避免了单继承的局限性。

    在定义线程时,建议使用实现方式。

两种方式的区别:

 继承Thread:线程代码存放在Thread子类run方法中

实现Runnnable,线程代码存在接口子类的run方法。

线程的安全问题 

   通过分析,发现,打印出0,-1,-2等错票

public class Test9 {  public static void main(String[] args) {   Ticket1 t = new Ticket1();   //开启四个线程   Thread t1 = new Thread(t);   Thread t2 = new Thread(t);   Thread t3 = new Thread(t);   Thread t4 = new Thread(t);   t1.start();   t2.start();   t3.start();   t4.start();  } }

class Ticket1 implements Runnable {  private int ticket = 100;//共享数据

 public void run() {

  while (true) {    if (ticket > 0) {     try {      Thread.sleep(10);     } catch (InterruptedException e) {      // TODO Auto-generated catch block      e.printStackTrace();     }     System.out.println(Thread.currentThread().getName() + "sale"       + ticket--);//有负数的存在,安全隐患    }   }  } }

以上多线程的运行出现了安全问题。

    问题原因:

         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。

          导致共享数据的错我。

   解决办法:

        对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

   java对于多线程的安全问题提供了专业的解决方式。

    就是同步代码块。 哪些代码需要同步,就看哪些语句在操作共享数据。

    synchronized(对象){

           需要被同步的代码

    }

如下面的解决方案。
 

public class Test9 {
	public static void main(String[] args) {
		//开启4个线程
		Ticket1 t = new Ticket1();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Ticket1 implements Runnable {
	private int ticket = 1000;

	// Object obj=new Object();
	public void run() {

		while (true) {
		//同步代码块,加锁     对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。、
			synchronized (this) {
				if (ticket > 0) {
					try {
						Thread.sleep(10);//让线程休眠
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()
							+ "sale" + ticket--);//没有出现负数票
				}
			}
		}
	}
}

 对象如同锁,持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

火车上的卫生间

同步的前提:

1,必须要有两个或者两个以上的线程。

2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。

好处:解决多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。允许消耗范围内的。

同步有两种表现形式,第一个是同步代码块,第二是同步函数。把synchronized作为修饰符放在函数上。

public class Test9 {  public static void main(String[] args) {   //开启4个线程   Ticket1 t = new Ticket1();   Thread t1 = new Thread(t);   Thread t2 = new Thread(t);   Thread t3 = new Thread(t);   Thread t4 = new Thread(t);   t1.start();   t2.start();   t3.start();   t4.start();  } }

class Ticket1 implements Runnable {  private int ticket = 1000;

 // Object obj=new Object();  public void run() {

  while (true) {   //同步代码块,加锁     对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。、    synchronized (this) {     if (ticket > 0) {      try {       Thread.sleep(10);//让线程休眠      } catch (InterruptedException e) {       // TODO Auto-generated catch block       e.printStackTrace();      }      System.out.println(Thread.currentThread().getName()        + "sale" + ticket--);//没有出现负数票     }    }   }  } }

public class Test10 {
   
    public static void main(String[] args) {
    	 Tickets t=new Tickets();
    	 //创建4个线程卖票,并开启
    	    Thread t1=new Thread(t);
    	    Thread t2=new Thread(t);
    	    Thread t3=new Thread(t);
    	    Thread t4=new Thread(t);
    	    t1.start();
    	    t2.start();
    	    t3.start();
    	    t4.start();
	}
}
class Tickets implements Runnable{
     private int tick=1000;//票数
     //复写run方法调用show
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			this.show();
		}
	}
	//同步函数所持有的锁是this
	public synchronized void show(){
		if(tick>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属的对象,就是this.

同步函数的使用的锁是this  

验证:使用两个线程来买票。

一个线程在同步代码块中。

一个线程在同步函数中。

都在执行买票动作。

public class Test10 {

	public static void main(String[] args) {
		Tickets t = new Tickets();
		// 创建4个线程卖票,并开启
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		/*
		 * Thread t3=new Thread(t); Thread t4=new Thread(t);
		 */
		t1.start();// t1一开启跑到同步代码块中。,开启这个线程不一定立即执行。处于临时状态,有可能执行下面一句
		t.flag = false;// 在t2开启之前,把标识变为false;
		try {
			Thread.sleep(10);// 主线程停止10毫秒,只能是t1在运行。过了时间段,可能执行下面的语句
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();// t2一开启跑到同步函数中。
		/*
		 * t3.start(); t4.start();
		 */
	}
}

class Tickets implements Runnable {
	private int tick = 1000;// 票数
	// 复写run方法调用show
	Object obj = new Object();
	boolean flag = true;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		if (flag) {
			while (true) {
				// 同步代码块
				// synchronized(obj),存在安全问题
				synchronized (this) {
					if (tick > 0) {
						try {
							Thread.sleep(10);
							System.out.println("同步代码块");
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		} else {
			while (true) {
				show();
			}
		}
	}

	// 同步函数
	public synchronized void show() {
		if (tick > 0) {
			try {
				Thread.sleep(10);
				System.out.println("同步函数");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


 

同步代码块使用的是任意的对象

 如果同步函数被静态修饰后,使用的锁是什么呢?

不再是this了。因为静态方法也不可以定义this.

静态进内存,内存中没有本类对象,但是一定有该类对应对应的字节码对象。

类名.class 该对象的类型是Class.

静态同步函数的锁:

   静态的同步函数使用的锁是该函数所属字节码文件对象

  可以使用getClass方法获取,也可以用当前类名.class表示

   静态的同步方法使用的锁是该方法所在类的字节码对象。

   验证方法如下:

public class Test10 {
	public static void main(String[] args) {
		Tickets t = new Tickets();
		// 创建4个线程卖票,并开启
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		/*
		 * Thread t3=new Thread(t); Thread t4=new Thread(t);
		 */
		t1.start();// t1一开启跑到同步代码块中。,开启这个线程不一定立即执行。处于临时状态,有可能执行下面一句
		t.flag = false;// 在t2开启之前,把标识变为false;
		try {
			Thread.sleep(10);// 主线程停止10毫秒,只能是t1在运行。过了时间段,可能执行下面的语句
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();// t2一开启跑到同步函数中。
		/*
		 * t3.start(); t4.start();
		 */
	}
}

class Tickets implements Runnable {
	private static int tick = 1000;// 票数
	// 复写run方法调用show
	Object obj = new Object();
	boolean flag = true;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		if (flag) {
			while (true) {
				// 同步代码块
				// synchronized(obj),存在安全问题
				synchronized (Test10.class) {
					if (tick > 0) {
						try {
							Thread.sleep(10);
							System.out.println("同步代码块");
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		} else {
			while (true) {
				show();
			}
		}
	}

	// 同步函数
	public synchronized void show() {
		if (tick > 0) {
			try {
				Thread.sleep(10);
				System.out.println("同步函数");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

   懒汉式单例模式  

     加入同步为了解决线程安全问题

    加入双重判断是为了解决效率问题

 

public class Test10 {
	public static void main(String[] args) {
		System.out.println("hello");
	}
}

class SingleDemo {
	private static SingleDemo s = null;// 共享数据,多个线程并发访问getInstance(),有可能存在安全问题,多条语句操作

	private SingleDemo() {// 私有构造函数
	}

	public static SingleDemo getInstance() {
		if (s == null) {
			synchronized (SingleDemo.class) {// 锁是字节码文件对象
				if (s == null) {
					s = new SingleDemo();// 对象延迟加载
				}
			}
		}
		return s;
	}
}



以上饿汉式不说,单说懒汉式。 

多个线程并发访问getInstance(), 多条语句操作共享数据s,存在安全隐患。

懒汉式:实例延迟加载,多线程访问存在安全隐患。加同步来解决,加同步函数,和同步代码块都行。但是稍微有些低效,

用双重判断可以解决效率问题。加同步使用的锁是哪个,该类所属的字节码对象

线程死锁的案例

 两个对象互相依赖,所以死锁!示例代码如下:
 

public class Test10 implements Runnable {
	public int flag = 1;
	static Object o1 = new Object(), o2 = new Object();//两个锁
	public void run() {
		System.out.println("flag=" + flag);
		//两个锁相持不下
		if (flag == 1) {
			synchronized (o1) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println("1");
				}
			}
		}
		if (flag == 0) {
			synchronized (o2) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o1) {
					System.out.println("0");
				}
			}
		}
	}

	public static void main(String[] args) {
		Test10 td1 = new Test10();
		Test10 td2 = new Test10();
		//定义标识
		td1.flag = 1;
		td2.flag = 0;
		//两个线程开启
		Thread t1 = new Thread(td1);
		Thread t2 = new Thread(td2);
		t1.start();
		t2.start();
	}
}


线程间的通讯:

其实就是多个线程在操作同一个资源,

但是操作的动作不同。
 

//资源 class Res {  String name;  String sex; } //添加的方法 class Input implements Runnable {  private Res r;

 public Input(Res r) {   this.r = r;  }

 public void run() {   // TODO Auto-generated method stub   int x = 0;//标识变量如果是0添加女,如果是1,添加男   while (true) {    synchronized (r) {//同步代码块添加,不加同步执行权可能被输出抢走。锁可以是资源对象。     if (x == 0) {      r.name = "丽丽";      r.sex = "女";     } else {      r.name = "mike";      r.sex = "man";     }     x = (x + 1) % 2;    }   }  } } //输出类 class Output implements Runnable {  private Res r;//资源对象

 public Output(Res r) {   this.r = r;  }  public void run() {   // TODO Auto-generated method stub   while (true) {    synchronized (r) {//这里也必须要加线程,操作共享数据,同一个锁,可以是资源对象     System.out.println(r.name + "..." + r.sex);//输出是统一资源    }   }  } }

public class Test11 {

 public static void main(String[] args) {   //形象的比喻  有一堆煤,有两个大卡车,一个进的,一个出的,把煤放到大卡车上,把卡车放到高速公路上。   Res r = new Res();   Input in = new Input(r);   Output out = new Output(r);   //开启两个线程   Thread t1 = new Thread(in);   Thread t2 = new Thread(out);   t1.start();   t2.start();  } }

以上的代码还是有问题的,按理说应该是存一个打印一个这样是比较靠谱的。上面的情况是一大片一大片的男,或者女。为什么出现这种情况?

    输入的线程如果获得了cpu执行权,它存了一个值后,其他线程进不来,这个时候出了同步,output,input都有可能抢到cpu执行权。所以输入有可能还会抢到,前面的值就回被覆盖掉了。当某一时刻,执行权被抢走了,输出被抢到了,他也可能把一个值打印多遍,所以造成了上面的情况。cpu切换造成的。现在需求是这样的,添加一个,取出一个,这样才是最靠谱的。为了满足条件需求,我们要做的是,在资源中加入一个标记,默认false;输入线程在往里面添加数据时,判断标记,false则存入,存完后,输入线程可能还持有执行权,将标记改为真,代表里面有数据了。为true时,不能在存入了,这个时候,让输入线程等着不动,wait()放弃了执行资格;当取走了之后,才能醒,notify()。

当output具备执行权的时候,开始输出,之前也要进行判断,如果true,取出,打印,变为false还持有执行权,回来之后,为false,wati(),叫醒 input,input等的时候,再把output叫醒。等待唤醒机制。

wait();

notity();

notityAll();

都是用在同步中。因为要对持有监视器(锁)的线程操作。

所以要使用同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义在Object中呢?

因为这些方法在操作同步线程时,都必须要标识它们所操作线程只有的锁。

只有同一个锁上的被等待线程,可以被同一个锁上notity唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

class Res {
	String name;
	String sex;
	boolean flag = false;// 标记是否有 资源
}
//添加类,实现Runnable接口
class Input implements Runnable {
	private Res r;//资源对象

	public Input(Res r) {// 关联资源对象
		this.r = r;
	}
   //重写run方法
	public void run() {
		// TODO Auto-generated method stub
		int x = 0;// 这里也必须要加线程,操作共享数据,同一个锁,可以是资源对象
		while (true) {
			synchronized (r) {
				if (r.flag) {
					try {
						r.wait();// 等待
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				if (x == 0) {
					r.name = "丽丽";
					r.sex = "女";
				} else {
					r.name = "mike";
					r.sex = "man";// 线程结束后,有可能还能抢到cpu执行权
				}
				x = (x + 1) % 2;
				r.flag = true;
				r.notify();// 唤醒线程池中的最早wait的线程。
			}
		}
	}
}
//输出类实现Runnable接口 
class Output implements Runnable {
	private Res r;

	public Output(Res r) {//关联资源对象
		this.r = r;
	}
    //重写run方法
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			synchronized (r) {
				if (!r.flag) {
					try {
						r.wait();// 等待,取消了执行资格
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println(r.name + "..." + r.sex);
				r.flag = false;
				r.notify();// 叫醒线程池中的最早线程
			}
		}
	}
}
public class Test {
	public static void main(String[] args) {
		Res r = new Res();
		// 形象的比喻 有一堆煤,有两个大卡车,一个进的,一个出的,把煤放到大卡车上,把卡车放到高速公路上。
		Input in = new Input(r);
		Output out = new Output(r);
		// 开启两个线程
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}

优化后的代码:

class Res {
	private String name;
	private String sex;
	boolean flag = false;
    //设置添加方法
	public synchronized void set(String name, String sex) {
		if (flag) {
			try {
				this.wait();//线程等待,没有执行资格
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		this.sex = sex;
		flag = true;//有了数据,标识变为true;
		this.notify();//唤醒线程池中的最早wait的线程
	}
     //输出方法
	public synchronized void out() {
		if (!flag) {
			try {
				this.wait();//线程等待,没有执行资格
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(name + ".." + sex);//打印
		flag = false;//取走了数据,变为false;
		this.notify();//唤醒线程池中的最早wait的线程
	}
}
//添加类,input
class Input implements Runnable {
	private Res r;

	public Input(Res r) {//关联资源
		this.r = r;
	}
    //重写run方法
	public void run() {
		// TODO Auto-generated method stub
		int x = 0;
		while (true) {

			if (x == 0) {
				r.set("mike", "man");
			} else {
				r.set("丽丽", "女");
			}
			x = (x + 1) % 2;

		}
	}

}
//输出类
class Output implements Runnable {
	private Res r;

	public Output(Res r) {//关联资源
		this.r = r;
	}

	public void run() {
		// TODO Auto-generated method stub
		while (true) {

			r.out();
		}
	}
}

public class Test {
	public static void main(String[] args) {
		Res r = new Res();//资源对象
		//创建两个线程,并开启
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();

	}
}



生产者与消费者的例子。出现更多线程运行程序。以上的例子会出现问题。

public class Test {  public static void main(String[] args) {   Resource r = new Resource();//创建资源对象   Producer pro = new Producer(r);//生成者   Cousumer con = new Cousumer(r);//消费者   //创建4个对象,并开启   Thread t1 = new Thread(pro);   Thread t2 = new Thread(pro);   Thread t3 = new Thread(con);   Thread t4 = new Thread(con);   t1.start();   t2.start();   t3.start();   t4.start();  } } //资源类 class Resource {  private String name;  private int count = 1;// 编号  private boolean flag = false;//判断标记    //赋值方法给  添加资源时调用  public synchronized void set(String name) {   while (flag) {    try {     this.wait();//线程等待,不具执行资格了。    } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }   this.name = name + "--" + count++;   System.out.println(Thread.currentThread().getName() + "............生产者"     + this.name);   flag = true;//输出了之后,没有资源了,标记改为true;   this.notifyAll();//唤醒线程池全部线程  }

 public synchronized void out() {   while (!flag) {    try {     this.wait();线程等待,不具执行资格了。    } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }   System.out.println(Thread.currentThread().getName() + "..消费者"     + this.name);   flag = false;//输出了之后,没有资源了,标记改为false;   this.notifyAll();//线程等待,不具执行资格了。  } } //生产者类,添加商品 class Producer implements Runnable {  private Resource res;    public Producer(Resource res) {//生成者关联资源对象   this.res = res;  }     //重写run方法。  public void run() {   // TODO Auto-generated method stub   while (true) {    res.set("+商品+");//赋值,添加产品   }  } } //消费者类,取走商品 class Cousumer implements Runnable {// 消费者  private Resource res;

 public Cousumer(Resource res) {   this.res = res;  }

 public void run() {   // TODO Auto-generated method stub   while(true){     res.out();//输出   }  }

}

 

当出现多个生产者消费的时候,必须要有while循环,要用notifyAll,用原来的就不行,这个是比较通用的。

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。

为什么定义notifyALL.

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。

Lock接口

 

解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。


到了后期版本,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。

在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。


所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。


在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。


而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是

Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。

< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();

  成功的 lock 操作与成功的 Lock 操作具有同样的内存同步效应。

 成功的 unlock 操作与成功的 Unlock 操作具有同样的内存同步效应

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值