JavaSE基础复习六:多线程

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


创建线程两种方法:继承Thread类或者实现Runnable接口

一:继承Thread类。 

步骤: 
1、定义一个类继承Thread类。 
2、覆盖Thread类中的run方法。 
3、直接创建Thread的子类对象创建线程。 
4、调用start方法开启线程并调用线程的任务run方法执行。 
通过getName()方法获取线程名,默认名称:Thread-编号(从0开始) 

二:实现Runnable接口。 

1、定义类实现Runnable接口。 
2、覆盖接口的run()方法,将线程的任务代码封装到run()方法中。 
3、创建Thread对象,将Runnable接口的子类对象作为Thread类构造函数的参数传递。  
4、调用Thread对象的start()方法开启线程。

Thread :如果没有父类可以继承Thread
Runnable :避免了单继承的局限性,建议采用实现接口方式创建线程

线程安全问题产生原因:

1、多个线程操作共享数据时。
2、操作共享数据的代码有多条代码。
在一个线程操作的过程中,其他线程可以参与进来。


线程状态转换


synchronized  同步锁:

同步代码块:锁是一个对象;  
同步函数:锁其实是this;
静态同步函数:锁是 类名.class  它是一个Class类的字节码文件对象

/*
卖票程序
同步代码块形式
*/

class Ticket implements Runnable {  //接口没有抛出异常,所以run()方法内只能捕捉异常
	
	private int num = 100;
	Object obj = new Object();
	
	public void run() {

		while(true) {
			try{
				Thread.sleep(100);//---测试多线程调用时,数据num出现负数
			}catch(InterruptedException e) {
				System.out.println("线程出错了");	
			}			
			synchronized(obj){
				if(num>0){
					System.out.println(Thread.currentThread().getName() + "----- sale ----" + num--);
				}
			}	
		}	
	}	
}

class TicketDemo {
	public static void main(String[] args) {
		
		Ticket t = new Ticket();//---只有一个Ticket对象
		Thread t1 = new Thread(t);
//		Thread t2 = new Thread(new Ticket());
//		Thread t3 = new Thread(new Ticket());
//		Thread t4 = new Thread(new Ticket()); //---4个Ticket对象需要使用static修饰num
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
			
	}	



举例:火车上的卫生间

同步的好处:解决了线程的安全问题。 
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。 
同步的前提:有多个线程并使用同一个锁。


单例设计模式:

使类只有一个对象(构造函数私有化,在类中创建本类对象并私有化为静态,定义公共静态方法,通过调用返回对象)
饿汉式:对象在类进入内存中时加载。不存在线程安全的问题
懒汉式:对象延迟加载。使用同步代码块,锁是类名.class,并在锁外在进行一次判断(双层判断)。可以防止加锁浪费资源。
/*
多线程下的单例设计模式

*/
//---饿汉式
class SingleHg {
	private static final SingleHg s = new SingleHg();
	private SingleHg(){}
	
	public static SingleHg getInstance(){
		return s;	
	}
}

//---懒汉式
class SingleLz {
	private static SingleLz s = null;
	private SingleLz(){}
	
	public static SingleLz getInstance(){
		if(s==null)
			s = new SingleLz();
		return s;	
	}
}

//---加同步锁的懒汉式,面试常考,一般实际用饿汉式

class Single {
	private static Single s = null;
	private Single(){}
	
	public static /*synchronized*/ Single getInstance(){ //---使用同步函数会降低效率,每次都要获取锁
		if(s==null)
			synchronized(Single.class){
				if(s==null)
					s = new Single();
			}	
		return s;	
	}
}



死锁程序:

Interrupt();强制清除线程的冻结状态,使线程获取执行资格,但是会使线程抛出InterruptedException


线程间通信:其实是多个线程在操作同一个资源,只是操作的动作不同;

考虑同步问题,加入等待唤醒机制;wait()  notify()
wait();notify();notifyAll(); 这些方法都应该在同步代码中使用,对持有锁的线程进行操作。
这些方法都定义在Object类中,所有对象都可以是锁,这些方法只能唤醒或者等待持有相同锁的线程。


生产者消费者问题:

多个生产者消费者,需要用while()循环判断标记;

class Duck {
	private int id;
	Duck(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@Override
	public String toString() {
		return "Duck [id=" + id + "]";
	}	
}

class DuckBasket {
	int index = 0;
	Duck[] ducks = new Duck[5];
	
	public synchronized void push(Duck duck) {
		while(index==ducks.length)
			try {
				System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
				this.wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		ducks[index] = duck;
		System.out.println(Thread.currentThread().getName()+"生产了"+duck);			
		index++;
		notify();
	}
	
	public synchronized Duck pop() {
		while(index==0)
			try {
				System.out.println(Thread.currentThread().getName()+":空了,快生产烤鸭!");
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		index--;
		//---打印输出用到了共享数据,要放到同步代码中
		System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);		
		notify();
		return ducks[index];
	}
}

class Producer implements Runnable {
	DuckBasket db;
	public Producer(DuckBasket db) {
		super();
		this.db = db;
	}
	
	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			Duck duck = new Duck(i);
			db.push(duck);		
		}
	}
}

class Consumer implements Runnable {
	DuckBasket db;
	public Consumer(DuckBasket db) {
		super();
		this.db = db;
	}
	
	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			db.pop();
		}
	}	
}

public class ProducerConsumerDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DuckBasket db = new DuckBasket();
		Producer pro1 = new Producer(db);
		Consumer con1 = new Consumer(db);
		Thread t1 = new Thread(pro1,"生产者----1");
		Thread t2 = new Thread(con1,"消费者1");
		Thread t3 = new Thread(pro1,"生产者----2");
		Thread t4 = new Thread(con1,"消费者2");
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}

}


新特性,多线程的升级解决方案,将同步的synchronized 升级为显示的lock(接口),
将wait(),notify(),notifyAll()升级为condition对象,该对象可以通过lock锁获取。
可以实现只唤醒对方的操作。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Duck {
	private int id;
	Duck(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@Override
	public String toString() {
		return "Duck [id=" + id + "]";
	}	
}

class DuckBasket {
	int index = 0;
	Duck[] ducks = new Duck[5];
	Lock lock = new ReentrantLock();
	Condition proL = lock.newCondition();
	Condition conL = lock.newCondition();
	
	public void push(Duck duck) {
		lock.lock();
		try{
			while(index==ducks.length)
				try {
					System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
					proL.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			ducks[index] = duck;
			System.out.println(Thread.currentThread().getName()+"做了"+duck);			
			index++;
			conL.signal();
		}finally{
			lock.unlock();
		}
	}
	
	public Duck pop() {
		lock.lock();
		try {
			while(index==0)
				try {
					System.out.println(Thread.currentThread().getName()+":空了,快做烤鸭!");
					conL.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			index--;
			//---打印输出用到了共享数据,要放到同步代码中
			System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);		
			proL.signal();
			return ducks[index];
		} finally{
			lock.unlock();
		}
	}
}

class Producer implements Runnable {
	DuckBasket db;
	public Producer(DuckBasket db) {
		super();
		this.db = db;
	}
	
	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			Duck duck = new Duck(i);
			db.push(duck);		
		}
	}
}

class Consumer implements Runnable {
	DuckBasket db;
	public Consumer(DuckBasket db) {
		super();
		this.db = db;
	}
	
	@Override
	public void run() {
		for(int i=0; i<20; i++) {
			db.pop();
		}
	}	
}

public class ProConLock {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DuckBasket db = new DuckBasket();
		Producer pro1 = new Producer(db);
		Consumer con1 = new Consumer(db);
		Thread t1 = new Thread(pro1,"生产者----1");
		Thread t2 = new Thread(con1,"消费者1");
		Thread t3 = new Thread(pro1,"生产者----2");
		Thread t4 = new Thread(con1,"消费者2");
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}



如何停止线程?

1、控制循环,做一个标记,使标记改变时满足条件就停止循环,结束run()方法;
2、如果线程处于等待(冻结)状态,无法结束线程,可以使用interrupt()方法强制解除冻结状态,进入运行状态,这样就可以操作标记,结束线程。


wait()和sleep()相同点,都可以使线程进入冻结状态,都抛出异常。

区别:
1、wait()为Object的方法,sleep()为Thread的方法
2、wait()必须有锁
3、wait()需要notify()唤醒,sleep()时间到了自动获取CPU执行资格

其他方法

join()方法,当a线程执行到了b线程的join方法时,a等待b执行完在继续执行。


setDaemon();守护进程,前台结束就结束


Thread类的 toString()方法,返回  [程名称,优先级,线程组]


setPriority()设置优先级  MAX_PRIORITY  MIN_PRIORITY NORMAL_PRIORITY   setName()  


yield();静态方法:暂停当前线程执行


匿名内部类创建进程---常用。


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

你可能感兴趣的:(JavaSE基础复习六:多线程)