多线程通信


1.包(package):包名具有唯一性。

在windows中的具体体现:文件夹。
1.包的作用:1.分类管理类文件;
2.对类提供了多层名称空间。
2.如何定义:使用关键字 package,定义必须定义在源代码的第一行,类名的全称是:包名.类名;
包名全小写如mypackage.
3.步骤:1.javac -d 包的路径 全类名.java
2.set classpath=包的路径;

3.java 包名.类名

一个包中不能存在两个public修饰的类。
被访问的包中,类和方法都需要被public修饰,否则权限不够。

4.包与包之间的继承
protected:包的修饰符,保护权限,只有子类才能用;用于保护父类中的成员。
protected 保护子类使用:必须子类才可以用,非子类则不可以用。

四种权限:  public protected   default(默认的)private
同一个类中:  ok    ok      ok   ok
同一个包中:  ok            ok            ok 
子类:      ok            ok
不同包中:      ok
    
5.简化书写:使用import关键字导入,格式:import 全类名;导入的都是包中的类;若是需要导入多个类,则使用通配符,如 import packa.*。
而在真实开发时,用到哪个则导入哪个,根本也用不到导入,用高级编辑器。
6.注意:
1.import packa.* :只导入packa包的当前目录下的所有类,而不导入当前目录下的包。
2.当导入的包中的类有重名时,使用该类时,必须用包名.类名的全名称,否则无法区分,会产生使用的不确定性。
  如 import packa.Demo;
     import packb.Demo;

不能new Demo(),而需new packa.Demo();

2.Jar包:java的压缩包。
-c:创建新的归档文件
-x:解压
java压缩工具:jar cvf haha.jar pack
jar包的基本使用。只要将类和包都存储到jar中,方便于使用。只要将jar配置到classpath路径下。 
  压缩:jar cf 归档名.jar 包名
  解压:jar -xvf 归档名.jar

3.多线程技术
1.进程:正在进行中的程序。
2.线程(Thread):负责进程进行执行。一个进程至少有一个线程,若有多个则称为多线程程序;线程也可以称为执行线程、执行路径、执行背景。
3.好处:可以同时执行多个程序。
4.JVM多线程的基本体现:
JVM也是多线程,至少有一个主线程和垃圾回收线程。主线程执行的是主函数中的内容,垃圾回收线程执行的对堆内存进行内存释放的代码。
5.垃圾回收的方法:finalize(),垃圾回收
 gc():运行垃圾回收器,System.gc();启动后不一定立即执行,因为CPU在随机做着切换,先切换到哪就先执行哪个。
6.自定义线程:
    通过API Thread类的查阅,两种方式:
1.继承Thread类,覆写run()方法,创建Thread子类对象,调用start方法来开启线程并调用run方法;线程类在描述线程时,有一个功能函数run,专门  用于封装执行的任务。
 主线程也是同理,该线程执行的任务在主函数中。
     
2.定义一个实现Runnable接口的类,然后该类实现 run 方法。然后可以分配该类的实例,在创建线程时作为一个参数来传递并启动。
1.定义类实现Runnable接口;
  2.覆写该接口中的run()方法;
  3.通过Thread类创建线程对象;
  4.将实现Runnable接口的实现类的对象作为参数传递给Thread类的构造函数,将run方法所属的对象传递给线程,让线程去执行我们指定的代码任务;
  5.调用start方法,目的是开启线程并调用run方法。
     
  Thread t=new Thread();创建进程。
1.覆写run方法的原因:创建进程的目的是为了运行指定的代码,任务就应该自己指定。

2.特定方法start():开启线程并调用run方法运行线程。
start和run区别:调用run方法,并没有开启新的线程,都是由主线程来执行,无法完成同时运行的需求;
       调用start方法,开启了新的线程并调用了线程的任务方法run方法,实现了代码同时执行。

3.执行路径是在每一条路径上先进后出的,即每个线程都有自己单独的一片栈内存空间。
4.currentThread():返回当前正在执行的进程,Thread.currentThread().getName();结果是Thread-编号,编号从0开始。

5.线程是分区来进行管理的,当前线程发生异常只影响当前线程,不影响其它线程。
6.主线程是最后结束的线程,只要还有存活的线程,它就不能结束。
7.创建线程的目的:为了开辟一个执行路径去执行指定的代码,让其和其它代码同时运行;意味着每一个线程的创建都有自己要运行的内容,而这个内容也称为线程的任务。

7.线程的随机性的导致的原因?
一个CPU在处理多个线程时,进行着随机的快速切换,切换到哪个线程就执行哪个线程,从而出现了随机性。

8.什么时候使用多线程?以及创建线程的目的?
     目的:为了开辟一个执行路径去执行指定的代码,让其和其它代码同时运行;意味着每一个线程的创建都有自己要运行的内容,而这个内容也称为线程的任务。

     使用:为了让多个执行路径同时进行,各自运行各自的内容,就可以使用多进程。

4.线程的四种状态:被创建、运行、冻结notify、sleep、wait、消亡(还有一个特殊状态:就绪)

每一个状态的特点?
  被创建:调用start()方法,开启线程并调用run()方法;
  运行:该状态是持有执行资格和CPU的执行权;
  冻结:既不具备CPU的执行权,也不具备执行资格;运行状态时调用了sleep()方法,当时间到了才转回运行状态;
  消亡:运行状态的run方法执行完成或者调用stop()方法;释放内存。

5.了解



6·Runnable接口出现的好处
  1.避免了单继承的局限性(线程对象和线程任务的耦合性很强);
  2.按照面向对象的思想,将线程运行的任务进行单独的对象封装,降低了线程对象和线程任务的耦合性。
 
  即:继承Thread类时,子类对象是一个线程对象;而在实现Runnable接口时,子类对象是一个线程任务,创建新的线程对象,
  将要执行的线程任务作为参数传递给线程对象,通过Thread类的构造函数将线程对象和线程任务关联起来。
		class Demo implements Runnable
		{
			//将线程要运行的任务代码封装到了该run方法中。 
			public void run()
			{
				for(int x=1; x<=20; x++)
				{
					System.out.println(Thread.currentThread().getName()+"......"+x);
				}
			}
		}
		class ThreadDemo4
		{
			public static void main(String[] args) 
			{
				Demo d = new Demo();


				//创建线程对象。因为没有了Thread的子类。所以通过Thread类来完成线程对象的创建。
				Thread t1 = new Thread(d);
				Thread t2 = new Thread(d);
				t1.start();//开启了线程并调用了线程中的run方法。只不过线程类中的run方法并没有定义我们要让线程运行的内容。 
				t2.start();

7·四个窗口售票的案例
	 class Ticket implements Runnable
	 {
	 	private int num = 100;
	 //	为了给同步代码块指定一个对象。可以用自定义的,也可以用已有的。 比如Object
	 	private Object obj = new Object();
	 	public void run()
	 	{
	 		while(true)
	 		{
	 			synchronized(obj)//这个对象就相当于一个锁。
	 			{
	 				if(num>0)
	 				{
	 					try{Thread.sleep(10);}catch(InterruptedException e){}//让当前线程在这里小睡一下。
	 					System.out.println(Thread.currentThread().getName()+"......"+num--);
	 				}
	 			}
	 		}
	 	}
	 }

	 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();
	 	}
	 }
内存图分析:


8·线程的安全问题

产生原因分析:1.多个线程在处理同一共享数据;
  2.线程任务代码中有多条语句在操作这个共享数据,这些多条操作共享数据的语句被线程分开进行执行,在一个线程执行这些语句的过程中,其它线程参与了运算导致数据的错误。

  线程任务中只要存在着:共享数据、多条操作共享数据的语句,就会产生安全问题。

9·安全问题的解决
  解决思想:对多条操作共享数据的语句,只能让一个线程都执行完,执行过程中,其它线程不可参与执行;
  将多条操作共享数据的语句进行封装,只有一个线程在内执行,运行期间其它进程进不来,只有这个线程执行完,其它线程才有执行的机会。
  方式:通过synchronized(对象){需要同步的代码;}需要同步的代码包括所有操作共享数据的代码,对象可以自定义,也可以是已有的,对象出来后再释放给
  同步代码块。

10·同步的好处:解决了多线程的安全问题;
  弊端:降低了效率,同一时刻只能有一个线程运行;而且易出现死锁。
  前提:必须是多个线程,使用的是同一个锁对象。

11·同步函数:在函数上加入同步关键字synchronized;
  同步函数和同步代码块的区别:
  1.同步函数在写法上较简洁;
  2.同步函数的锁对象是固定的,而同步代码块的锁对象是任意的,建议用同步代码块,尤其是在代码中使用多个同步不同锁时,必使用同步代码块。

同步函数,和静态同步函数使用的锁是:
   同步函数:对象.getClass();
   静态:类名.class

12·懒汉式:当多线程并发访问时,会出现多线程的安全问题,无法保证对象的唯一性,这时用同步函数来解决这个问题,但同时产生新的问题:降低性能。
   解决性能降低的方法:通过双重判断的格式,写同步代码块且在代码块前加if判断语句,好处是:减少判断锁的动作,相对提高了性能。
 	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;
		}
	}
	class Test
	{
		public static void main(String[] args) 
		{
			System.out.println("Hello World!");
		}
	}

13·死锁:
  常见情况之一:同步的嵌套,即同步中还有同步,而锁对象不同。
  一个运行在flag为true的同步代码块中,另一个运行在flag为false的同步函数中,让同步函数中有同步代码块,同步代码块中有同步函数。
	class Test implements Runnable
	{
		private static final Object lock1=new Object();
		private static final Object lock2=new Object();
		private boolean flag;
		Test(boolean flag)
		{
			this.flag=flag;
		}
		public void run()
		{
			if (flag)
			{
				while (true)
				{
					synchronized(lock1)
					{
						System.out.println("if ....lock1 ");
						synchronized (lock2)
						{
							System.out.println("if ....lock2");
						}
					}
				}
			}
			else 
			{
				while (true)
				{
					synchronized(lock2)
					{
						System.out.println("else ....lock2 ");
						synchronized (lock1)
						{
							System.out.println("else ....lock1");
						}
					}
				}
			}
		}
	}
	class DeadLockTest 
	{
		public static void main(String[] args) 
		{
			Test t1=new Test(true);
			Test t2=new Test(false);


			Thread ta=new Thread(t1);
			Thread tb=new Thread(t2);
			ta.start();
			tb.start();
		}
	}

14·线程间通信:有多个线程在处理同一个资源,但是处理的动作却不一样。(一进一出)
  示例:资源中有姓名和性别,一个线程负责赋值,一个线程负责取值。
  //创建资源对象;
  //创建线程任务对象;
  //创建线程对象;
  //开启线程。
  分析:input:在资源中定义标记用于判断资源是否有值,boolean flag=false;当标记为true,说明有值,input应该等待,让output运行。
   output:当资源中标记为false,说明没值,output执行输出,输出完改标志flag=true,


16·多线程间通信最为重要的机制:等待唤醒机制。涉及方法:wait(),notify(),notifyAll().
 
  wait():可以让线程处于冻结状态,释放执行资格和执行权。同时将线程存储到一个线程池中,wait前先释放锁。
  notify():只唤醒线程池中的一个线程,随机任意的。

  nitifyAll():唤醒线程池中的所有线程,让所有线程都具备执行资格。

  

  这些方法就是用在锁上的,都必须使用在同步中,因为同步中带有锁,同步可以有多个,但只有一个同步锁,用同步锁来区分。
  所以在使用时,必须要明确wait的是哪个锁上的线程,notify的是哪个锁上的线程,调用时用锁对象来调用。
 
  为什么这些方法定义在Object中?这些方法都用在同步中,而同步锁是任意对象,任意对象都能用的方法应该定义在Object类中。

17·生产者消费者案例
    多生产者和多消费者:
问题:发生数据的错乱。
原因:被唤醒的线程没有去判断标记,直接进行了执行。
解决:用while循环,当它被notify后,再去判断一下标记。
新问题:本方在唤醒过程中可能唤醒到本方线程,本来只需唤醒对方一个线程就可以了,导致效率过低。
解决:用新版本,将锁对象与监视器分离,一个锁对象绑定两组监视器,一组监视生产者,一组监视消费者。


18·多线程技术的升级:JDK1.5版本中提供了一个java.util.concurrent.locks.Lock接口,对锁进行单独的描述个封装,并定义操作的锁的方法在锁对象中。
  同步代码块和同步函数:对锁的操作是隐式的;
  Lock接口:显示操作锁。

  用Lock接口代替同步后,不能再有wait().notify().notifyAll()等方法。
  而提供了Condition接口,将操作锁上线程的状态方法(等待,唤醒,唤醒所有)进行了单独的封装,都封装到了Condition对象中。
  Condition改写:以前要解决,希望有两组监视器放分别监视不同的线程,一组监视生产者,一组监视消费。
  但是之前监视器方法和锁是绑定的,两组监视器需要两个锁,很有可能出现同步嵌套,很容易出现死锁.

  但改写后问题依旧存在,本方还是一样会唤醒全部(本方).
  解决方案:一个锁对象,绑定多组监视器。一组监视生产者,一组监视消费者。

  好处:以前是一个监视器方法上只能有一个锁,现在一个锁能有多个监视器方法。      
 	     import java.util.concurrent.locks.*;
	     class Res
	     {
	       	private String name;
	       	private int count = 1;
	       	private boolean flag;
	       	//创建一个显示锁对象。
	       	private Lock lock = new ReentrantLock();
	       	//获取到该lock锁上监视器对象。该监视器对象负责监视生产者。
	       	private Condition producer_con = lock.newCondition();
	       	private Condition consumer_con = lock.newCondition();
	       	
	       	public  void set(String name)// 
	       	{
	       		// 获取锁。显示方法 
	       		lock.lock();
	       		try
	       		{
	       			while(flag)
	       				try{producer_con.await();}catch(InterruptedException e ){}//t0(t2将其唤醒  wait) t1(被t0唤醒了 wait)
	       			
	       			this.name = name+count;//商品-1  商品-2  商品-3
	       			
	       			count++;//2 3 4


	       			System.out.println(Thread.currentThread().getName()+"...生产者...."+this.name);//生产 商品-1  生产 商品-2 生产 商品-3


	       			flag = true;
	       			consumer_con.signal();
	       		}finally{
	       			//释放锁。
	       			lock.unlock();
	       		}
	       	}
	       	public  void out()// 
	       	{
	       		lock.lock();
	       		try
	       		{
	       			while(!flag)
	       				try{consumer_con.await();}catch(InterruptedException e ){}//t2(wait) t3{wait)


	       			System.out.println(Thread.currentThread().getName()+"........消费者......."+this.name);//消费 商品-1
	       			flag = false;
	       			producer_con.signal();
	       		}
	       		finally
	       		{
	       			lock.unlock();
	       		}
	       	}
	       }


	     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.out();
	       		}
	       	}


	     }
	     class ProConDemo2 
	     {
	       	public static void main(String[] args) 
	       	{
	       		Res r = new Res();
	       		Producer pro = new Producer(r);
	       		Consumer con = new Consumer(r);
	       		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();
	       	}
	     }


19·sleep和wait的区别:

  1.sleep:必须指定睡眠时间;
   wait:可以指定可以不指定,如果没指定,必须用notify唤醒。
  2.在同步中:
  sleep:释放执行权,但不释放锁对象,因为会自动醒;
  wait:释放执行权,还会释放锁。
  3.wait:必须用在同步中;
   sleep:可以不定义在同步中。

20. 停止线程  
方式:只要run方法结束,线程就停止,而run方法中通常有循环结构,所以只要控制住循环条件即可;
控制方式:定义一个boolean类型的变量flag来作为标记,定义一个功能来改变flag值。
特殊情况:当在while循环中加入wait方法,由于开启的线程都可能被wait,这样线程就不能结束。
解决方法:用interupt方法,可以将被wait的线程从冻结状态强制恢复到运行状态;
应用场景:用于sleep的时间还没到就需要结束线程,或者wait没有被notify,即强制唤醒,但因其强制,所以存在异常;
改进:将改标记的操作加入到catch块中,当interupt发生异常后,处理异常时,改标记flag=false;再回去读标记时,结束循环。

21.setDaemon方法:参数boolean型,将线程设为守护线程或者用户线程;
特点:具有依耐性,只要前台线程结束,后台线程自动结束。

toString():返回线程名称、线程优先级和线程组。
优先级:1-10;最小优先级1,默认优先级5,最大优先级10.如果都在抢CPU,则优先级一样;
线程组:可以统一操作一组线程。

setPriority:设置线程优先级,如setPriority(Thread.MAX_PRIORITY)。
join():临时加入一个线程进行执行,当主线程持有执行权执行到t1的join方法时,说明t1要加入运行,这是主线程会释放出执行权和执行资格,t1就开始执行,主线程不依赖时间和唤醒,当t1线程执行结束,主线程自动获得执行资格;但如果t1中有wait方法,则线程就不能结束。

yield():释放当前线程的执行权,让其他线程有机会具备执行权;


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值