黑马程序员_多线程及单例模式

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

 

进程与线程:

 

进程,正在运行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径

 

,或者叫一个控制单元。

 

线程,就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。

 

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

 

线程都有自己的默认名称,也可以通过setName()方法,或者通过构造方法来给线程设置自定义的名称。

 

 

启动线程的两种方式:

 

第一种方式:

 

继承Thread类:


1、定义类继承Thread。


2、复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。


3,调用线程的start方法,该方法两个作用:启动线程,调用run方法。


Thread类用于描述线程。


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

 

也就是说Thread类中的run方法,用于存储线程要运行的代码。

 

class Demo extends Thread {

	@Override
	public void run() { //重写Thread的run()方法
		
		System.out.println("demo run");
	}
	
	
}

	public static void main(String[] args) throws IOException {

		Demo demo = new Demo();
		
		//开启线程的同时并执行该线程下的run方法,run方法并不能启动一个新的线程。
		demo.start();
	}

 

 

 

 

第二种方式:


实现Runnable接口:


1、定义类实现Runnable接口


2、覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

 

3、通过Thread类建立线程对象。


4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。


 为什么要将Runnable接口的子类对象传递给Thread的构造函数。


 因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去执行对象的run方

 

法。就必须明确该run方法所属对象。


5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

 

 

public class Demo {

	public static void main(String[] args) throws IOException {

		Demo d = new Demo();
		
		Thread t1 = new Thread(d);//创建线程,并将Runnable接口的子类对象传递给Thread
		
		t1.start();//启动线程
		
	}

}

class Demo implements Runnable {

	@Override
	public void run() {//实现接口的run方法
		
		System.out.println("demo run");
	}
}

 

 

两种方式区别:

 

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

 

2、继承方式的线程代码存放在Thread子类的run方法中。

 

3、实现方式的线程代码存放在接口的子类的run方法中。

 

 

线程状态分析图:


黑马程序员_多线程及单例模式

 

 

多线程的安全问题:

 

导致安全问题的出现的原因:

 

 

1、多个线程访问出现延迟。

 

 

 

2、线程随机性。

 

 

同步(synchronized):

 

1、同步代码块:

 

synchronized(对象)

{

         需要同步的代码;

}

 

2、同步函数

 

         1、同步函数的锁是this

 

         2、如果同步函数被静态修饰,则锁为Class

 

同步的前提:

 

1、同步需要两个或者两个以上的线程。

 

2、多个线程使用的是同一个锁。

 

未满足这两个条件,不能称其为同步。

 

同步的弊端:

 

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的

 

运行效率。

 

 

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();//锁对象,锁可以是任何对象,任何对象又都继承于Object
	public void run()
	{
		while(true)
		{
			synchronized(obj)//同步代码块
			{
				if(tick>0)
				{
					System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
				}
			}
		}
	}
}

 

 

同步中出现的死锁现象:

 

两把锁嵌套使用则有可能出现死锁。

 

class Test implements Runnable
{
	private boolean flag;//标志位
	
	Test(boolean flag)//构造方法中传入一个标志初始化值
	{
		this.flag = flag;
	}

	public void run()
	{
		if(flag)//如果flag为true,则进入同步代码块,拿到锁locka,若此时另一个线程已经开始运行,并flag的值为false,则拿到了锁lockb
		{			// 当拿有locka锁的线程要进入b锁的代码块时,lockb却被另一个线程持有,而此时持有lockb锁的线程也在等待locka锁,这样发生嵌套的情况,
					//即会发生死锁情况。
			while(true)
			{
				synchronized(MyLock.locka)
				{
					System.out.println(Thread.currentThread().getName()+"...if locka ");
					synchronized(MyLock.lockb)
					{
						System.out.println(Thread.currentThread().getName()+"..if lockb");					
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)
				{
					System.out.println(Thread.currentThread().getName()+"..else lockb");
					synchronized(MyLock.locka)
					{
						System.out.println(Thread.currentThread().getName()+".....else locka");
					}
				}
			}
		}
	}
}


class MyLock
{
	static Object locka = new Object();//定义locka锁对象
	static Object lockb = new Object();//定义lockb锁对象
}

class  DeadLockTest
{
	public static void main(String[] args) 
	{
		//创建并启动两个线程
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

  

 

线程间通信:

 

就是多个线程在操作同一个资源,但是操作的动作不同。以下用生产者消费者的示例演示线程间通信方法,生产一个消费一个。

 

class IOtest1 {
	
	public static void main(String[] args) throws InterruptedException {
		
		/**
		 * 创建4个线程同时执行,其中两个生产者和两个消费者
		 * 但商品依然是生产一个消费一个的方式进行
		 */
		Resource r = new Resource();
		
		Producer pro = new Producer(r);
		
		Consumer con = new Consumer(r);
		
		Thread t1 = new Thread(pro);
		
		Thread t2 = new Thread(con);
		
		Thread t3 = new Thread(pro);
		
		Thread t4 = new Thread(con);
		
		//启动这4个线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}

class Resource {
	
	private String name;
	
	private int count = 1;
	
	private boolean flag = false;
	
	/**
	 * 资源生产方法
	 * @param name
	 */
	public synchronized void set(String name) {
		
		while(flag){ //if和while区别:当等待中的线程再次被唤醒时,if不需要再次判断标志位,直接向下执行,而while需要重新判断标志位
			
			try {
				this.wait();//线程等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name + "..." + count++;
		
		System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);//打印生产
		
		flag = true;
		this.notifyAll(); //notify和notifyAll方法:前者只能唤醒先进入线程池中的,并且持有该锁的线程,而后者可以唤醒线程池中所有持有该锁的线程
	}
	
	/**
	 * 资源消费方法
	 */
	public synchronized void out() {
		
		while(!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"...消费者"+this.name);//打印消费
		
		flag = false;
		this.notifyAll();
	}
}

/**
 * 生产者类
 * @author Administrator
 *
 */
class Producer implements Runnable {

	private Resource res;
	
	Producer(Resource res) {
		
		this.res = res;
	}
	
	@Override
	public void run() {

		while(true) {
			
			res.set("-商品-");//调用生产方法
		}
	}
	
}

/**
 * 消费者类
 * @author Administrator
 *
 */
class Consumer implements Runnable {
	
	private Resource res;
	
	Consumer(Resource res) {
		
		this.res = res;
	}
	
	@Override
	public void run() {

		while(true) {
			
			res.out();//调用消费方法
		}
	}
}

 

 

wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中:

 

1,这些方法存在与同步中。


2,使用这些方法时必须要标识所属的同步的锁。

 

3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

 

 

wait(),sleep()有什么区别:

 

wait():释放cpu执行权,释放锁。

 

sleep():释放cpu执行权,不释放锁。

 

 

 


停止线程:


只有run方法结束后,线程便会停止。

 

开启多线程运行,运行代码通常是循环结构,只要控制好循环,就可以让线程停止。

 

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

 

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

 

Thread类提供该方法 interrupt();

 


守护线程:
当正在运行的线程都为守护线程时,jvm将退出。
setDaemon方法可以设置为守护线程。必须在启动线程之前执行。
 
Join方法
T1.join();当前线程释放执行权,并进入冻结状态,让T1执行。

 

1.5版本JDK中的线程锁:

 

private Lock lock = new ReentrantLock();
	
	Condition con = lock.newCondition();

 

 

一个Lock中可以有多个Condition

 

 

单例设计模式:

 

 

单例设计模式:解决一个类在内存只存在一个对象。

 

保证对象唯一:

 

1、为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象


2、还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。


3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

 

代码体现过程:

 

1、将构造函数私有化。


2、在类中创建一个本类对象。


3、提供一个方法可以获取到该对象。

 

 

饿汉式:

 

private static 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) {
					s = new Single();
				}
			}
		}
		return s;
	}
}

 

 

 

 

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