多线程

 

1、进程:正在进行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径。或者叫一个控制单元。其实进程就是一个应用程序运行时的内存分配空间。

线程:其实就是进程中一个程序执行控制单元,一条执行路径。线程在控制着进程的执行。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。线程是程序中的执行线程,java虚拟机允许应用程序并发地运行多个执行线程。

 

一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。

JVM启动的时候会有一个进程Java.exe,该进程中至少有一个线程负责Java程序的执行而且这个线程运行的代码存在于main方法中。该线程称之为主线程

扩展:其实更细节说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程。

因为当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。

随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。

返回当前线程的名称:Thread.currentThread().getName()

线程的名称是由:Thread-编号定义的。编号从0开始。线程要运行的代码都统一存放在了run方法中。线程要运行必须要通过类中指定的方法开启。start方法。(启动后,就多了一条执行路径)

start方法:1)、启动了线程;2)、让jvm调用了run方法。

如何在自定义的代码中,自定义一个线程

通过对api的查找,Java已经提供了对线程这类事物的描述,就是Thread类

创建线程的第一种方式:继承Thread,由子类复写thread类的run方法。

步骤:

1,定义类继承Thread类;

2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;

3,通过创建Thread类的子类对象,创建线程对象;

4,调用线程的start方法,开启线程,并执行run方法。

发现运行结果每一次都不同:

因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。(也就是随机性的原理)明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺CPU的执行权。这就是多线程的一个特性:随机性。谁强到谁执行,至于执行多长,CPU说了算。

为什么要覆盖run方法呢?

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

class Demo extends Thread//定义类继承Thread类
{
	public void run()//目的是复写run方法,将要让线程运行的代码都存储到run方法中;
	{
		for(int x=0; x<60; x++)
			System.out.println("demo run----"+x);
	}
}
class ThreadDemo 
{
	public static void main(String[] args) 
	{
		//for(int x=0; x<4000; x++)
		//System.out.println("Hello World!");
		Demo d = new Demo();//创建好一个线程。
		//d.start();//开启线程并执行该线程的run方法。
		d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
		for(int x=0; x<60; x++)
			System.out.println("Hello World!--"+x);
	}
}

练习:

创建两个线程,和主线程交替运行。

分析:原来线程都有自己默认的名称。Thread-编号 该编号从0开始。static Thread currentThread():获取当前线程对象。getName(): 获取线程名称。设置线程名称:setName或者构造函数。

class Test extends Thread
{
	//private String name;
	Test(String name)
	{
		//this.name = name;
		super(name);
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
		}
	}
}
class ThreadTest 
{
	public static void main(String[] args) 
	{
		Test t1 = new Test("one---");
		Test t2 = new Test("two+++");
		t1.start();
		t2.start();
//		t1.run();
//		t2.run();
		for(int x=0; x<60; x++)
		{
			System.out.println("main....."+x);
		}
	}
}

线程状态:

被创建:start()

运行:具备执行资格,同时具备执行权;

冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;

临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;

消亡:stop()

Sleep方法需要指定睡眠时间,单位是毫秒。

一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。

线程状态图:

 

创建线程的第二种方式:实现runnable接口

步骤:

1.定义类实现runnable接口

2,覆盖runnable接口中的run方法

      将线程要运行的代码存放在该run方法中

3,通过thread类建立线程对象。

4,将runnable接口的子类对象作为实际参数传递给thread类的构造函数

 为什么要将runnable接口的子类对象传递给thread的构造函数

因为,自定义的run方法所属的对象是runnable接口的子类对象,  所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。

5,调用thread类的start方法开启线程并调用runnable接口子类的run方法。

        Ticket t = new Ticket();

        直接创建Ticket对象,并不是创建线程对象。 因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。

         Thread t1 = new Thread(t); //创建线程。

   只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联

   为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。

          t1.start();

-------------------------------------------------------

//面试

        new Thread(new Runnable(){  //匿名

            public void run(){

                System.out.println("runnable run");

            }

        })

        {

            public void run(){

                System.out.println("subthread run");

            }

        }.start();  //结果:subthread run

---------------------------------------------------------

Try {

Thread.sleep(10);

}catch(InterruptedException e){}//当刻意让线程稍微停一下,模拟cpu切换情况。

 

为什么要有Runnable接口的出现?

1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。

可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?

只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。

所以,通常创建线程都用第二种方式。

 

因为实现Runnable接口可以避免单继承的局限性。

2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。

所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。

 

实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象

实现方式和继承方式有什么区别?考面试题

实现方式好处:避免了单继承的局限性,在定义线程时建议使用实现方式,

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

实现runnable:线程代码存放在接口的子类的run方法。

练习:需求:简单的卖票程序。多个窗口同时买票。

class Ticket implements Runnable//extends Thread
{
	private  int tick = 100;
	public void run()
	{
		while(true)
		{
			if(tick>0)
			{
				System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
			}
		}
	}
}
class  TicketDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		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();
		/*
		Ticket t1 = new Ticket();
		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();
		//Ticket t4 = new Ticket();
		t1.start();
		t1.start();
		t1.start();
		t1.start();
		*/
	}
}

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

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

多线程安全问题的原因:

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

解决办法:

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

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

就是同步代码块。

格式:

synchronized(对象)//任意对象都可以。这个对象就是锁。

{

    需要被同步的代码

}

对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

需求:简单的卖票程序。多个窗口同时买票。改进过的,解决打印出0,-1,-2等错票。

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					//try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
				}
			}
		}
	}
}
class  TicketDemo2
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		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();
	}
}

火车上的卫生间---经典。

同步

同步的前提:

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

2,必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

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

弊端:多个线程需要判断锁,较为消耗资源,产生了死锁。

练习:

需求:银行有一个金库,有两个储户分别存300员,每次存100,存3

目的:该程序是否有安全问题,如果有,如何解决?

如何找问题:

1,明确那些代码是多线程运行代码

2,明确共享数据

3,明确多线程运行代码中那些语句是操作共享数据。

class Bank
{
	private int sum;
	//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 Cus 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) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}

同步的第二种表现形式:

同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。

同步函数是用的哪个锁呢?

通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁

当同步函数被static修饰时,这时的同步用的是哪个锁呢?

静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象

所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。

这个对象就是 类名.class

同步代码块和同步函数的区别?

同步代码块使用的锁可以是任意对象。

同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象

在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

同步函数用的哪一个锁呢

 函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this。通过该程序进行验证

需求:使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中,都在执行买票动作。

class Ticket implements Runnable
{
	private  int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(this)
				{
					if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public synchronized void show()//this
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
		}
	}
}
class  ThisLockDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
//		Thread t3 = new Thread(t);
//		Thread t4 = new Thread(t);
//		t3.start();
//		t4.start();
	}
}

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

通过验证,发现不再是this,因为静态方法中也不可以定义this

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class 该对象的类型是Class。 静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class

class Ticket implements Runnable
{
	private static  int tick = 100;
	//Object obj = new Object();
	boolean flag = true;
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Ticket.class)
				{
					if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
		}
	}
}
class  StaticMethodDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}
}

单例设计模式

★考点问题:请写一个延迟加载的单例模式?写懒汉式;当出现多线程访问时怎么解决?加同步,解决安全问题;效率高吗?不高;怎样解决?通过双重判断的形式解决。

//懒汉式:延迟加载方式。

当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。所以容易出现线程安全问题。为了解决,加入同步机制,解决安全问题。但是却带来了效率降低。

为了效率问题,通过双重判断的形式解决。

class Single{

    private static Single s = null;

    private Single(){}

    public static Single getInstance(){ //锁是谁?字节码文件对象;

        if(s == null){

           synchronized(Single.class){

              if(s == null)

                 s = new Single();

           }

       }

        return s;

    }

}

懒汉式延迟加载,如果多线程访问时有安全问题,用同步锁解决,加同步使用的锁是该类所属的文件

public class SingleDemo {
	public static void main(String[] args) {
		}
}
//单例设计模式
// 饿汉式
/*class Single
 {
   private static final Single s=new Single();
   private Single(){}
   public static Single getInstance()
   {
	   return s;
   }
 }*/
//懒汉式 在多线程时,就产生安全隐患,加上同步比较低效,懒汉式实例的延迟加载, 在多线程时,就产生安全隐患,加上同步比较低效,有同步代码块和同步函数,
//用双重否定,来改变低效,加同步时,使用的锁是该类所属的字节码文件对象
class Single
{
  private static  Single s=null;
  private Single(){}
  public static  Single getInstance()
  {
	  if(s==null)
	  {
	     synchronized(Single.class)
	      {
	         if(s==null)
//		  --->A
//		  --->B
		     s=new Single();
	       }
	  }
	  return s;
  }
}

《《《《————————————》》》》

同步死锁通常只要将同步进行嵌套,就可以看到现象。同步函数中有同步代码块,同步代码块中还有同步函数。

死锁:

同步中嵌套同步,而锁不同。

public class DeadLockTest {
	public static void main(String[] args) {
	Thread t1=new Thread(new Test11(true));
Thread t2=new Thread(new Test11(false));
t1.start();
t2.start();
	}
}
class Test11 implements Runnable
{
  private boolean flag;
  Test11(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
{
 static  Object locka=new Object();
 static  Object lockb=new Object();
}

线程间通信思路:多个线程在操作同一个资源,但是操作的动作却不一样。

1:将资源封装成对象。

2:将线程执行的任务(任务其实就是run方法。)也封装成对象。

等待唤醒机制:涉及的方法:

wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。

notify唤醒线程池中某一个等待线程。

notifyAll:唤醒的是线程池中的所有线程。

注意:

1:这些方法都需要定义在同步中。

2:因为这些方法必须要标示所属的锁。

    你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。

3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?

    因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。

wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:

wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。

sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。

wait:线程会释放执行权,而且线程会释放锁。

Sleep:线程会释放执行权,但不是不释放锁。

等待唤醒机制

notifyAll();唤醒所有的等待。

 wait(),notify(),notifyAll()出现在同步里面。当前线程必须有此对象监视器。 wait()时要标识出哪个锁中的wait,例如r.wait()为何?因为同步会出现嵌套,wait()notify().notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。

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

 因为这些方法在操作同步中线程时,都必须要标识它们锁操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义object类中。

public class InputOutputDemo {	
	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
{
 String name;
 String sex;
 boolean flag=false;
}
class Input implements Runnable
{
  private Res r;
  Object obj=new Object();
  Input(Res r)
  {
	  this.r=r;
  }
  public void run()
  {
	   int x=0;
	 while(true)
	 {
	synchronized(r/*Input.class*/)
	{ 
		if(r.flag)
			try {
				r.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		      if(x==0)
	         {
		 r.name="mike";
		 r.sex="man";
	       }
	     else{
		 r.name="lilli";
		 r.sex="nv";
	     }
	    x=(x+1)%2;
	    r.flag=true;
	   r.notify();
	 }
	 }
  }
}
class Output implements Runnable
{
 private Res r;
 Output(Res r)
 {
	 this.r=r;
 }
 public void run()
 { while(true)
	 {
	 synchronized(r/*Input.class*/)
	 {  if(!r.flag)
		try {
			r.wait();
		} catch (InterruptedException e) {
						e.printStackTrace();
		}
		 System.out.println(r.name+"....."+r.sex);
	  r.flag=false;
	  r.notify();
	 }
 }
 }
}

《《《《——————————》》》》

代码优化

public class InputOutputDemo {
	public static void main(String[] args) {
	Res r=new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
/*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;
 private 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;
	 this.notify();
 }
 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;
 this.notify();
 }
}
class Input implements Runnable
{
  private Res r;
  Object obj=new Object();
  Input(Res r)
  {
	  this.r=r;
  }
  public void run()
  {
	   int x=0;
	 while(true)
	 {
		 if(x==0)
		 r.set("mike","man");
	     else
		 r.set("lilli","nv");
	    x=(x+1)%2;
	 }
  }
}
class Output implements Runnable
{
 private Res r;
 Output(Res r)
 {
	 this.r=r;
 }
 public void run()
 { while(true)
	 {
	  r.out();
 }
 }
}

《《《《————————》》》》

需求:多生成者和消费者情况

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

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

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

为什么定义notifyAll,

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

public class ProducerConsumerDemo {
	/**多生产者消费者的情况
	 * 当出现多个线程时,要用while标识和notifyAll
	 * @param args
	 */
	public static void main(String[] args) {
		Resource r=new Resource();
		Produce pro=new Produce(r);
		Consumer con=new Consumer(r);
    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)//用了while出现了全部等待
	try {
		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;
  this.notifyAll();
 }
 public synchronized void out()
 {  while(!flag)
	 try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	 System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
   flag=false;
   this.notifyAll();
 }
}
class Produce implements Runnable
{
 private Resource res;
 Produce(Resource res)
 {
	 this.res=res;
 }
 public void run()
 {
	 while(true)
	 {
		 res.set("商品++");
		 
	 }
 }
}
class Consumer implements Runnable
{
	 private Resource res;
	 Consumer(Resource res)
	 {
		 this.res=res;
	 }
	 public void run()
	 {
		 while(true)
		 {
			 res.out();
		 }
}
}

《《《《——————————》》》》

JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

将Object中的wait,notify notifyAll,替换了Condition对象。

该对象可以Lock锁 进行获取。

该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized

    lock

    unlock

    newCondition()

Condition:替代了Object wait notify notifyAll

    await();

    signal();

    signalAll();

多生产者和消费者

 

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2
	private Lock lock = new ReentrantLock();
	private Condition condition_pro = lock.newCondition();
	private Condition condition_con = lock.newCondition();
	public  void set(String name)throws InterruptedException
	{
		lock.lock();
		try
		{
			while(flag)
				condition_pro.await();//t1,t2
			this.name = name+"--"+count++;
			System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
			flag = true;
			condition_con.signal();
		}
		finally
		{
			lock.unlock();//释放锁的动作一定要执行。
		}
	}
	//  t3   t4  
	public  void out()throws InterruptedException
	{
		lock.lock();
		try
		{
			while(!flag)
				condition_con.await();
			System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
			flag = false;
			condition_pro.signal();
		}
		finally
		{
			lock.unlock();
		}
		
	}
}
class Producer implements Runnable
{
	private Resource res;
	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.set("+商品+");
			}
			catch (InterruptedException e)
			{
			}
			
		}
	}
}
class Consumer implements Runnable
{
	private Resource res;
	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.out();
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}

Jdk1.5中提供了多线程升级解决方案,将同步synchronized替换成现实lock操作

object中的waitnotifynotifyAll替换了condition对象

该对象可以lock锁,进行获取。该实例中,实现了本方只唤醒对方操作。

线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。

停止线程:原理就是:让线程运行的代码结束,也就是结束run方法。

怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。

第一种方式:定义循环的结束标记。因为线程运行代码一般都是循环,只要控制了循环即可。

第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。即使用interrupt(中断)方法,该方法是结束线程的冻结状态,使线程回到运行状态中来

特殊情况

当线程处于了冻结状态

就不会读取到标记,那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行消除。

强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束

Thread类提供该方法interrupt();

class StopThread implements Runnable
{
	private boolean flag =true;
	public  void run()
	{
		while(flag)
		{	
			System.out.println(Thread.currentThread().getName()+"....run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}
class  StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
		int num = 0;
		while(true)
		{
			if(num++ == 60)
			{
				//st.changeFlag();
				//t1.interrupt();
				//t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......."+num);
		}
		System.out.println("over");
	}
}

---------< java.lang.Thread >----------

interrupt()中断线程。

setPriority(int newPriority)更改线程的优先级。

getPriority()返回线程的优先级。

toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

Thread.yield()暂停当前正在执行的线程对象,并执行其他线程。

setDaemon(true)将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。

join临时加入一个线程的时候可以使用join方法。

当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。

Join方法

申请加入到CPU执行权。

特点:

A线程执行到了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。

《《《《——————————》》》》

toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组

System.out.println(Thread.currentThread().toString()+"...."+x);

默认优先级是5setPriority(int newPriority)

t1.setPriority(Thread.MAX_PRIORITY);                                            

t2.setPriority(Thread.MIN_PRIORITY);

Thread.yield();暂停当前正在执行的线程对象,并执行其他线程。

《《《《——————————》》》》

class MyThread extends Thread{
	public void run(){
		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
		}
		System.out.println("MyThread running");
	}
}
public class ThreadTest{
	public static void main(String argv[]) {
		MyThread t = new MyThread();
		t.run();
		t.start();
		System.out.println("Thread Test");
	  }
}

代码分析过程:

MyThread t = new MyThread();

创建了一个线程。

t.run();

调用MyThread对象的run方法。这是只有一个线程在运行就是主线程。当主线程执行到了run方法中的sleep(3000);时。这是主线程处于冻结状态。程序并没有任何执行。当3秒过后,主线程打印了  MyThread running。 run方法执行结束。

t.start();

开启了t线程。

有两种可能情况。

第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行,打印了Thread Test。主线程结束。t线程获取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。3秒后,打印MyThread running t线程结束,整个程序结束。

第二种情况:

主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。就调用自己的run方法。指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。那么主线程开始执行打印了Thread Test,主线程结束。等到3秒后,t线程打印MyThread running ,然后t线程结束。程序结束。

《《《《——————————》》》》

Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口

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

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

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

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

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

而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。

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

--------------------------------------------------------

class BoundedBuffer {

   final Lock lock = new ReentrantLock();

   final Condition notFull  = lock.newCondition();

   final Condition notEmpty = lock.newCondition();

   final Object[] items = new Object[100];

   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {

     lock.lock();

     try {

       while (count == items.length)

         notFull.await();

       items[putptr] = x;

       if (++putptr == items.length) putptr = 0;

       ++count;

       notEmpty.signal();

     }

    finally {

       lock.unlock();

     }

   }

   public Object take() throws InterruptedException {

     lock.lock();

     try {

       while (count == 0)

         notEmpty.await();

       Object x = items[takeptr];

       if (++takeptr == items.length) takeptr = 0;

       --count;

       notFull.signal();

       return x;

     }

finally {

       lock.unlock();

     }

   }

 }

                               ———寄语:走自己的路,让别人羡慕嫉妒恨去吧。瞄准自己的方向,勇往直前!加油!濛濛在努力中。。。。。——— 濛濛

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值