黑马程序员java学习笔记之四(java多线程总结)

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

 


 

在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

 

对于直接继承Thread的类来说,代码大致框架是:

class 类名 extends Thread 
{
	属性1;
	属性2;
	...

	方法1;
	方法2;
	...

	public void run()
	{
		// other code
	}

}


一个简单的示例:

class Hello extends Thread 
{
	private String name ;

	public Hello()
	{
	}

	public Hello(String name)
	{
		this.name = name;
	}

	public void run()
	{
		for(int i=1; i<30; i++)
			System.out.println(name + "运行   "+ i);
	}

}

class ThreadTest
{
	public static void main(String[] args){

		Hello h1 = new Hello("A");
		Hello h2 = new Hello("B");

		h1.start();
		h2.start();
	}
}

 

因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。

注意:虽然我们在这里启动线程调用的是start()方法,但是实际上调用的还是run()方法的主体。

 

通过实现Runnable接口:

大致框架是:

class 类名 implements Runnable  
{
	属性1 ;
	属性2 ;
	...
	
	方法1 ;
	方法2 ;
	...

	public void run()
	{
		// other code ;
	}
}


一个简单示例如下:

/**  
 * @author 冯兵兵 实现Runnable接口 
 *
 * */

class Hello implements Runnable
{
	private String name ;

	public Hello()
	{
	}

	public Hello(String name)
	{
		this.name = name;
	}

	public void run()
	{
		for(int i=1; i<10; i++)
			System.out.println(name + "运行   "+ i);
	}

}

class ThreadTest
{
	public static void main(String[] args){

		Hello h1 = new Hello("线程A");
		Hello h2 = new Hello("线程B");

		Thread t1 = new Thread(h1);
		Thread t2 = new Thread(h2);

		t1.start();
		t2.start();
	}
}

【可能的运行结果】:


关于选择继承Thread还是实现Runnable接口?

其实Thread也是实现Runnable接口的

public
class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,ThreadRunnable都实现了run方法,这种操作模式其实就是代理模式。
实现方式和继承方式有什么区别呢?

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

两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。利于资源的共享。

例如模拟一个买票程序:

/**  
 * @author 冯兵兵 模拟售票系统 
 *
 * */

class Ticket implements Runnable 
{
	private  int tick = 20;
	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);//创建了一个线程;
		
		t1.start();
		t2.start();
		t3.start();

	}
}

【可能的运行结果】:

总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

线程的休眠:

 

/**  
 * @author 冯兵兵 线程的休眠
 *
 * */

class Hello implements Runnable
{
	public void run()
	{
		for(int i=1; i<5; i++)
		{
			try{
				Thread.sleep(1000);
			}
			catch(Exception e)
			{
				e.printStackTrace(); 
			}
			System.out.println(Thread.currentThread().getName()+i);
		}
	}

}

class ThreadTest
{
	public static void main(String[] args){

		Hello h1 = new Hello();
		
		Thread t1 = new Thread(h1,"线程A");

		t1.start();
	}
}

运行结果:每隔1秒输出一个

线程A1

线程A2

线程A3

线程A4

 

线程的优先级:

/**  
 * @author 冯兵兵 线程的优先级
 *
 * */

class Hello implements Runnable
{
	public void run()
	{
		for(int i=1; i<5; i++)
		{
			System.out.println(Thread.currentThread().getName()+"运行"+i);
		}
	}

}

class ThreadTest
{
	public static void main(String[] args){

		Thread t1 = new Thread(new Hello(),"线程A");
		Thread t2 = new Thread(new Hello(),"线程B");
		Thread t3 = new Thread(new Hello(),"线程C");

		t1.setPriority(8); // 设置t1的优先级为8
		t2.setPriority(2); // 设置t2的优先级为2
		t3.setPriority(5); // 设置t3的优先级为5

		t1.start();
		t2.start();
		t3.start();
	}
}


可能运行结果:

 

注意:不要误以为优先级越高就先执行。谁先执行还是取决于谁先获得CPU的资源。

 

线程同步与死锁

class Sale implements Runnable {   

    private int count=5; 

	public void run() {         
		for(int i=0;i<10;++i){             
			if(count>0){                 
				try{                    
					Thread.sleep(1000);               
				}catch(InterruptedException e){                    
					e.printStackTrace();                
				}                 
				System.out.println(count--);            
			}         
		}    
	} 
	
	public static void main(String[] args) {     
		
		Sale s=new Sale();         
		Thread h1=new Thread(s);         
		Thread h2=new Thread(s);         
		Thread h3=new Thread(s);        
		h1.start();       
		h2.start();        
		h3.start();    
	}    
} 


运行结果:

5

4

3

2

1

0

-1

这里出现了-1,显然这个是错的。,应该票数不能为负值。

如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,

其他的线程必须等到这个线程结束之后才能继续执行。

【使用线程同步解决问题】

采用同步的话,可以使用同步代码块和同步方法两种来完成。

 

【同步代码块】:

语法格式:

synchronized(同步对象){

 //需要同步的代码

}

但是一般都把当前对象this作为同步对象。

class Sale implements Runnable {   

    private int count=5; 

	public void run() {         
		for(int i=0;i<10;++i){     
			synchronized(this){
				if(count>0){                 
					try{                    
						Thread.sleep(1000);               
					}catch(InterruptedException e){                    
						e.printStackTrace();                
					}                 
					System.out.println(count--);            
				} 
			}
		}    
	} 
	
	public static void main(String[] args) {     
		
		Sale s=new Sale();         
		Thread h1=new Thread(s);         
		Thread h2=new Thread(s);         
		Thread h3=new Thread(s);        
		h1.start();       
		h2.start();        
		h3.start();    
	}    
} 


【运行结果】:(每一秒输出一个结果)

5

4

3

2

1

 

【同步方法】

也可以采用同步方法。

语法格式为synchronized 方法返回类型 方法名(参数列表){

    // 其他代码

}

这里不再赘述。

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

将同步Synchronized替换成显式Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized
 lock
 unlock
 newCondition()

Condition:替代了Object wait notify notifyAll
 await();
 signal();
 signalAll();

当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

此处列举经典的生产者和消费者问题。

【生产者和消费者问题】

先使用传统的线程技术解决生产者消费问题,代码如下:

class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();

		Producer pro = new Producer(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;
			//  t1    t2
	public synchronized void set(String name)
	{
		while(flag)
			try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
		this.name = name+"--"+count++;

		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();
	}


	//  t3   t4  
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
		System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
		flag = false;
		this.notifyAll();
	}
}

class Producer implements Runnable
{
	private Resource res;

	Producer(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新特性来解决这个问题

import java.util.concurrent.locks.*;

class ProducerConsumerDemo2 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();

		Producer pro = new Producer(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;
			//  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)
			{
			}
		}
	}
}

 

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition 对象。 但是,当锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。

 

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

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值