java——线程(三)

       这一篇是线程的中级篇吧,还没有涉及到高级篇,高级篇,涉及到的原理比较多,中级篇,还是仅仅停留在多线程的使用上,只不过,使用的层次提高了很多。

      中级使用1.(将两种线程创建的方式合二为一)

                     new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					System.out.println("runable.run");
				}
			}){
				public void run() {
					System.out.println("Thread.run");
				};
			}.start();

  上面代码的输出的是结果是什么?输出的是Thread.run

            因为,new Thread(Runable.run){run()}.start();,new Threa(){run()}是一个Thread的子类对象,start(),jvm调用的是子类对象的run()方法,如果子类没有run()方法,则会调用父类的run()方法,父类的run()方法会调用Runable.run()方法。

     中级使用2.定时器

                //1.简单使用
		new Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("haha");
			}
		}, 1000);//一个定时器,调度(一个任务,设置时间)
		
		
        new Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("haha");
			}
		}, 1000,2000);//一个定时器,调度(一个任务,设置时间,间隔时间持续执行)
        
        //2.深入使用
        new Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("haha");
				new Timer().schedule(new TimerTask() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						System.out.println("xixi");
					}
				}, 1000,2000);
			}
		}, 1000);//一个定时器执行一个任务,这个任务内部调用另一个定时器

    中级使用3.互斥(synchronized)

     我们都知道synchronized是互斥,可以用于代码块和方法上,使其安全,就是说,一个线程在使用使,其他线程无法进行。没有什么,最重要的是使用相同的对象,才可以使之互斥。

     synchronized是一种互斥锁,一次仅允许一个线程进入被锁住的代码

     synchronized是一种内置锁/监视器锁,java中每个对象都有一个内置锁,通过使用内置锁将将代码锁住。

     synchronized保证了原子性和可见性。

    例子:当一个synchronized代码块,和一个静态的synchronized方法,如何保证两个互斥?

             要互斥,则必须使用相同的对象,如何保证对象相同呢?静态方法可以根据类名.方法名进行调用呢?所以,可以使用类.class,synchronized(类.class)类.class在内存中只有一份,所以可以使之互斥。

     这是我在网上找的线程视频,是张孝祥老师讲的视频,里面有个题,子线程循环10次,主线程100次,子线程在循环10次,主线程再循环100次,就这样总共循环50次。这个题本来也是没有什么思路,然后就看视频,把思路学了一下。下面是这个题的代码。

     写完这个代码,受教了。感觉对线程,有了更深的认识。对于线程互斥,组主要的还是那个锁对象,所以必须要找准那个对象。

public class ThreadDemo4 {
	//子线程循环10次,主线程100次,子线程在循环10次,主线程再循环100次,就这样总共循环50次
	
	public static void main(String[] args) {
		
		final DemoT demo=new DemoT();
		
		
			new Thread(new Runnable() {
				public void run() {
					for(int j=0;j<50;j++){//也就是说,如果demo被等待,线程的转态仅仅是运行变为阻塞,比如循环第20次等待,主线程
						demo.sub(j);//获得执行权,之后,子线程被唤醒,还是第二十次循环开始的,这并没有改变
					}
				}
			}).start();//子线程
			
			for(int i=0;i<50;i++){
				demo.mai(i);//主线程,可以看mai(),并没有创建一个线程,确确实实是main的主线程,因为mai()方法中有一个方法,
				            //this.wait(),相当于在main中调用了该方法,此时main主线程被等待,
				
			}
		}		
	
}

	class DemoT{
		private boolean a=true;
		public synchronized void sub(int j){
			//为ture时,循环,否则,等待
			while(!a){/这里while比if效果好,等待后,while会再次判断
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			for(int i=0;i<10;i++){
				System.out.println("子线程第"+i+"次"+"-------"+j);
			}
			a=false;
			this.notify();
		}
		
		public synchronized void mai(int j){
			//为false时,循环,否则,等待
			while(!a){
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			for(int i=0;i<100;i++){
				System.out.println("主线程第"+i+"次"+"-------"+j);
			}
			a=true;
			this.notify();
		}
	}

     写这个代码的时候,我在想,为什么不把循环50次的代码,也写到synchronized的方法体内?

          如果写进入synchronized方法体内,一个线程获得锁后,其他线程无法进入,将这样,外部循环一次,内部循环10次,直至循环50次。与我们的题目不符合。

        对象锁:要用到共同数据的若干个方法,应该归在同一个类中。体现了健壮性和高类聚。


  中级使用4.线程内的共享变量(就是一个线程绑定一个数据源,这个线程内的对象,都可以调用这个数据源。

class DemoThread{
		private static int data=0;
		private static Map map=new HashMap<>();
		public static void main(String[] args) {
			for(int i=0;i<2;i++){
				new Thread(new Runnable() {				
					@Override
					public void run() {
						// TODO Auto-generated method stub
						int data=new Random().nextInt();
						System.out.println(Thread.currentThread().getName()+"+++++"+data);
						map.put(Thread.currentThread().getName(), data);
						new A().get();
						new B().get();
					}
				}).start();
			}
		}
		
		static class A{
			public void get(){
				int data=(int)map.get(Thread.currentThread().getName());
				System.out.println(Thread.currentThread().getName()+"---"+data);
			}
		}
		static class B{
			public void get(){
				int data=(int)map.get(Thread.currentThread().getName());
				System.out.println(Thread.currentThread().getName()+"---"+data);
			}
		}
	}
 

      不同的线程具有不同的数据,就是一个Map,key值为线程名称。就是这种思想。上面只是一种模拟,当然,已经为我们提供了这样一个类,Threadlocal类,内部是一个Map集合,只需要设置值即可。当然,一个线程只能在Threadlocal绑定一个数据,但是我们可以将多个属性定义在一个类中,将这个类的对象设置值,放置在Threadlocal中就可以了,使用更简单。

//单例模式
	class MyThreadScopeData{
		private MyThreadScopeData(){}
		public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
			MyThreadScopeData instance = map.get();
			if(instance == null){
				instance = new MyThreadScopeData();
				map.set(instance);
			}
			return instance;
		}
		//private static MyThreadScopeData instance = null;//new MyThreadScopeData();//饱汉模式和饿汉模式
		private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
		/*
		 * 饱汉模式,对象还没有加载,就有了对象 static new
		 * 饿汉模式,只有要使用时,才创建对象
		 */
		private String name;
		private int age;
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
	}

     中级使用5.多线程之间的共享变量

         1.多个线程使用同一个实现了Runable的对象,前面买票案例就是这种方法。

         2.static修饰的变量,多个线程均可操作

         3.每个线程要操作的代码不同,需要将这几个方法封装在同一个实现了Runable类中,创建这个类的final的对象,创建线程时,传入不同的runable对象,在run()方法内,调用不同的方法。不同的是,run()方法内调用的方法不同,就这点区别。

         4.一个runable类A封装不同的方法和数据,另一个runable类B的run()调用new A中的一个方法,另一个runable类C的run()调用new A中的一个方法,然后就是Thread创建的时候使用B或C对象

  

          中级使用6.线程池Executors

       java5提供类线程池,线程池分为三种:固定线程池(线程数固定),缓存线程池(动态的,根据任务创建线程),单一线程池(只有一个线程)-----单一线程池,运行完后,线程死亡,再有任务,还会在创建一个线程。

       线程池的关闭:shotdown,所有任务完成后,关闭

                            shotdownNow,立即关闭,即使任务没有完成

        

        中级使用7.线程锁Lock,比synchronized更加面向对象。

                        线程锁还有读写锁,分为读锁,写锁,读写锁三种。

                         和Lock的用法差不读,读锁,读数据时,上读锁,写数据时,上写锁。


      中级使用8.condition

           在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

         一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。

          condition是基于Lock的,不能和synchronized联用的,感觉和wait()方法和notify()差不多,他的两个方法是await()和signal()方法,await()就是等待,signal()就是发送一个信号,唤醒一个线程。

          传统的唤醒和等待,可以使用两个线程间的通信,但是三个线程之间进行通信如何呢?如果还是使用传统的wait()和notify()方法已经不能满足了,所以可以使用condition,功能更加强大。

           三个线程之间的通信,可以创建三个condition对象,每个线程可以使用一个condition来控制线程的等待和唤醒。

 

       中级使用9.Semaphore的使用

         Semaphore信号灯,类似于,多人去银行取款(多个线程),2个柜台(2个信号灯),不管多少个人,取款的人数始终都是两(线程运行数始终都是2),一个人取款后(一个线程运行完后),再来一个人(线程)进行取款操作。常和线程池连用。

           acquire(),从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

           release(),释放一个许可,将其返回给信号量。


       这一篇是线程使用的中级篇,其实就是在使用方法上介绍了一下,并没有举例子。可以看一下jdk api,tomcat,缓存的底层都有线程的身影,线程可谓是基本。关于线程,我还想写一篇线程高级篇,就不在仅仅是线程的使用了,会更深入一点。这个初级,中级,高级,仅仅是根据自己的水平定义的,自己所涉及的线程使用还是很少的,嗯,就这样了。

     





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值