黑马程序员—多线程

<span style="background-color: rgb(255, 255, 255); font-family: Arial; font-size: 14px; line-height: 26px;"></span><pre name="code" class="plain">
</pre>
<pre name="code" class="sql">
------- Android培训 java培训 、期待与您交流! ----------
</pre>
/* 多线程:
 * 
 * 进程:
 * 是一个正在执行中的程序,每一个进程执行都有一个执行的顺序。该顺序是一个执行路径或者叫一个控制单元
 * CPU不断切换对进程进行执行
 * 
 * 线程:
 * 线程就是进程中一个独立的控制单元,线程在控制着进程的执行
 * 一个进程中至少有一个线程
 * 
 * 多线程存在的意义
 * 线程的创建方式
 * 多线程的特性
 */

/*
 * Java虚拟机启动的时候会有一个进程java.exe
 * 该进程中至少有一个线程在负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为
 * 主线程。
 */
class ThreadDemo
{
	public static void main(String[] args)
	{
		for(int x=0;x<4000;x++)
			System.out.println("hello world!");
	}
}
/* 
 * 启动时观察windows任务管理器发现:
 * 编译时javac.exe进程启动了,编译完又消失了
 * 运行时java.exe进程启动了,运行完也消失了
 * 
 * 说明了java有两个进程:编译进程、运行进程
 * 
 * 扩展:其实更细节说明虚拟机,虚拟机启动不止一个线程,除了主线程还有负责垃圾回收机制的线程 
 * 
 * 多线程的存在有什么意义呢?
 * 多线程可以让程序中的部分产生同时运行的效果,多线程的下载可以提高下载的效率
 * 
 */

/*
   如何在自定义的代码中去自定义一个线程呢?
   	  
   创建新执行线程有两种方法:
   	  
   1、创建线程的第一种方式:继承Thread类
   通过对API的查找,java已经提供了对线程这类事物的描述,就是Thread类
   Thread:线程。是程序中的执行线程。
      
    步骤:
   	(1)、定义类继承Thread
   
    (2)、复写Thread类中的run方法
   	  	 目的:将自定义代码存储在run方法中,让线程运行
   	  
    (3)、调用线程的start方法,start:使该线程开始执行,Java虚拟机调用该线程的run方法
         该方法有两个作用:a.启动线程   b.调用run方法
           
    2、创建线程的第二种方式:实现Runnable接口
 
    步骤:
    (1)、定义类实现Runnable

    (2)、覆盖Runnable接口中的run方法,将线程要运行的代码存放在run方法中
 
    (3)、通过Thread类建立线程对象
  
    (4)、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
         为什么要将Runnable接口的子类对象传递给Thread的构造函数?
         因为,自定义的run方法所属的对象是Runnable接口的子类对象
         所以要让对象去执行指定对象的run方法。就必须明确该run方法所属的对象
      
    (5)、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法  
    
    一般鼓励使用第二种方法,因为Java里面只允许单一继承,但允许实现多个接口。第二个方法更加灵活。
     
    
 */
class Demo1 extends Thread
{
	public void run()//run是用来封装线程要运行的代码
	{
		System.out.println("demo run");
	}
}
class ThreadDemo1
{
	public static void main(String[] args)
	{
		Demo1 d=new Demo1();//创建好一个线程
		d.start();//开启线程,并执行该线程的run方法
	}
}
/*
 * 运行结果为:
 * demo run
 */

/*
 * 以下程序是一个多线程程序。两个for循环会交替执行,此时CPU在切换执行。所以双核CPU运行速度快
 * 
 * 多线程程序:  main 
 *               ↓
 *         Demo2 d=new Demo2();
 *             d.start();——————┐
 *               ↓                 ↓
 *            hello world         demo run
 * 打印结果是交替打印的
 */
class Demo2 extends Thread
{
	public void run()
	{
		for(int x=0;x<40;x++)
			System.out.println("demo run"+x);
	}
}
class ThreadDemo2
{
	public static void main(String[] args)
	{
		Demo2 d=new Demo2();
		
		//d.run();//仅仅是对象调用方法,而线程Demo1 d=new Demo1();创建了却并没有运行,执行完run
		          //再执行下面的程序
		d.start();//开启线程并执行该线程的run方法,
		
		for(int x=0;x<60;x++)
			System.out.println("hello world"+x);
	}
}
/*
 * 以上程序发现运行结果每一次都不同
 * 因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行
 * 明确一点,在某一个时刻,只能有一个程序在运行(多核除外)
 * CPU在做着快速的切换,以达到看上去是同时运行的效果
 * 我们可以形象的把多线程的运行行为看成是在互相抢夺CPU的执行权
 * 
 * 这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,CPU说的算
 * 
 * 为什么要覆盖run方法呢?
 * Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法
 * 也就是说Thread类中的run方法,用于存储线程要运行的代码。
 * 
class ThreadDemo extends Thread
{
	public static void main(String[] args)
	{
		Thread t=new Thread();
		
		t.start();
	}
}
以上这种调用没意义
 */


/*
练习:
创建两个线程,和主线程交替运行
*/
class Test3 extends Thread
{
	public void run()
	{
		for(int x=0;x<40;x++)
		{
			System.out.println(" run....."+x);
		}
	}
}
class ThreadTest3
{
	public static void main(String[] args)
	{
		Test3 t1=new Test3();
		Test3 t2=new Test3();
		t1.start();//开启第一个线程
		t2.start();//开启第二个线程
		
		for(int x=0;x<60;x++)
			System.out.println("main....."+x);
	}
}
/*
 * 运行结果发现两个线程都在交替运行,但区分不出分别是哪个线程运行的。
 * 
 * 所以为了区分出2个线程的执行情况,给两个线程分别起一个名字
 */


class Test extends Thread
{
	private String name;
	Test(String name)
	{
		this.name=name;
	}
	public void run()
	{
		for(int x=0;x<40;x++)
		{
			System.out.println(name+" run....."+x);
		}
	}
}
class ThreadTest
{
	public static void main(String[] args)
	{
		Test t1=new Test("one");//创建第一个线程,为了使这个线程清晰,传一个"one"
		Test t2=new Test("two");//创建第二个线程,为了使这个线程清晰,传一个"two"
		t1.start();//开启第一个线程
		t2.start();//开启第二个线程
		
		for(int x=0;x<60;x++)
			System.out.println("main....."+x);
	}
}
/*
 * 以上程序开启线程将start换成用run方法,就会执行完"one run....x"再执行"two run....x",
 * 最后执行"main....."就不会交替执行了
 */

/*
 * 创建线程方式一:
 * 继承Thread类
 * 1、子类覆盖父类中的run方法,将线程运行的代码存放在run中
 * 2、建立子类对象的同时,线程也被创建
 * 3、通过调用start方法开启线程
 * 
 * 
 * 原来线程都有自己默认的名称,"Thread-编号",该编号从0开始,获取方法:getName()
 */
class Test1 extends Thread
{
	public void run()
	{
		for(int x=0;x<40;x++)
		{
			System.out.println(this.getName()+" run...."+x);
		}
	}
}
class ThreadTest1
{
	public static void main(String[] args)
	{
		Test1 t1=new Test1();//即使加入自定义名称显示的还是默认名称Thread—编号
		Test1 t2=new Test1();//即使加入自定义名称显示的还是默认名称Thread—编号
		t1.start();
		t2.start();
		for(int x=0;x<60;x++)
			System.out.println("main....."+x);
	}
}
/*
 * 以上两个线程执行的是默认名称Thread-编号,我们如何才能让线程显示自己定义的名称"one"和"two"呢?
 * Thread类中有Thread(String name)方法,可以直接继承父类Thread类的这个方法就可以显示自定义名称了。
 * 
 * static Thread currentThread:获取当前线程对象
 * 设置线程名称:setName 或者 构造函数
 */
class Test2 extends Thread
{
	Test2(String name)
	{
		super(name);//继承Thread类中定义名称的方法
	}
	public void run()
	{
		for(int x=0;x<40;x++)
		{
			/*Thread.currentThread()==this,写哪一个都可以
             *其中"Thread.currentThread()"表示运行Thread类中的currentThread()线程*/
			System.out.println(this.getName()+" run....."+x);
		}
	}
}
class ThreadTest2
{
	public static void main(String[] args)
	{
		Test2 t1=new Test2("one");
		Test2 t2=new Test2("two");
		t1.start();
		t2.start();
		for(int x=0;x<60;x++)
			System.out.println("main....."+x);
	}
}
/*
 * 以上程序两个线程执行run方法中的for循环,所执行的x是不是一个x?
 * 不是!两个线程在进入run方法中的for循环时,内存会给两个线程分别开辟2个内存空间,每个空间都有一个
 * run方法,每个run方法中都有一个x
 */


/*
 * 需求:简单的卖票程序
 * 多个窗口同时卖票
 */
class Ticket extends Thread
{
	private static int tick=100;//这儿如果不加static就会每个线程都出现卖100张票的情况
	public void run()
	{
		/*
		 * 如果单看while中的条件:true.这段程序应该是一个无限循环,但当while循环里中有return x,
		 * 那么当程序执行到return的时候,就回自动跳出循环,并返回x.
		 */
		while(true)
		{
			if(tick>0)
			{
				System.out.println(this.getName()+"...sale:"+tick--);
			}
			/*
			 * break的作用简单说就是跳出当前的整个循环,下面有个简单说明: 
			 * break语句可以强迫程序中断循环,当程序执行到break语句时,即会离开循环,继续执行
			 * 循环外的下一个语句,如果break语句出现在嵌套循环中的内层循环,则break语句只会跳出
			 * 当前层的循环; 
			 */
			else
				break;
		}
	}
}
class TicketDemo
{
	public static void main(String[] args)
	{
		Ticket t1=new Ticket();
		Ticket t2=new Ticket();
		Ticket t3=new Ticket();
		Ticket t4=new Ticket();
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

/*
 * 虽然将票数定义成静态可以,但是一般不建议用静态,因为生命周期过长,那该如何写呢?
 * 可以将以上代码运用“创建线程的方式二”来创建
 * 
 * 创建线程的第二种方式:实现Runnable接口
 * 步骤:
 * 1、定义类实现Runnable接口
 * 
 * 2、覆盖Runnable接口中的run方法
 *    将线程要运行的代码存放在该run方法中
 *    
 * 3、通过Thread类建立线程对象
 * 
 * 4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
 *    为什么要将Runnable接口的子类对象传递给Thread的构造函数?
 *    因为,自定义的run方法所属的对象是Runnable接口的子类对象
 *    所以要让对象去执行指定对象的run方法。就必须明确该run方法所属的对象
 * 
 * 5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
 * 
 * 实现方式和继承方式有什么区别呢?(经常会考的面试题!)
 * 
 * 实现方式好处:避免了单继承的局限性,例:如果Student类是Person的子类,他就不能再继承Thread类。
 *              如果用Runnable接口就比较方便
 * 在定义线程时,建议使用实现方式,这种方法是最常用的。但如果建立的类没有自己的父类用继承也可以
 * 
 * 两种方式的区别:
 *      继承Thread类:线程代码存放在Thread子类run方法中
 *      实现Runnable:线程代码存放在Runnable接口子类的run方法中
 */
class Ticket1 implements Runnable
{
	private int tick=100;//共享数据
	public void run()
	{
		while(true)
		{
			if(tick>0)//线程执行的多条语句
			{
				System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
			}
			else
				break;
		}
	}
}
class TicketDemo1
{
	public static void main(String[] args)
	{
		Ticket1 t=new Ticket1();//这个对象不是线程,因为它和Thread类没关系
		
		//方法:Thread(Runnable target)表示能接收一个Runnable接口类型的对象
		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();
	}
}


//但以上程序卖票系统会出现安全问题,打印出了0、-1、-2等错票
class Ticket2 implements Runnable
{
	private int tick=100;//线程共享数据
	public void run()
	{
		while(true)
		{
			if(tick>0)//线程执行的多条语句
			{
				try
				{
					Thread.sleep(10);
				}
				catch(Exception e)
				{
					
				}
				System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
			}
			else
				break;
		}
	}
}
class TicketDemo2
{
	public static void main(String[] args)
	{
		Ticket2 t=new Ticket2();//这个不是线程
		
		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();
	}
}
/*
 * 以上程序通过分析发现,打印出了0,-1,-2等错票
 * 多线程的运行出现了安全问题
 * 
 * 问题的原因:
 * 		当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完
 * 		另一个线程参与进来执行,导致了共享数据的错误
 * 
 * 解决办法:
 * 		对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行
 * 
 * JAVA对于多线程的安全问题提供了专业的解决方式,就是同步代码块
 * synchronized(对象)		
 * {
 * 		需要被同步的代码
 * }
 * 哪些代码需要被同步,就看哪些语句需要操作共享数据。即不能同时有多个线程执行
 * 
 * 对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程,即使获取了CPU的执行权也进不去,
 * 因为没有获取锁。火车上的卫生间——经典!
 * 
 * 同步的前提:
 * 1、必须要有两个或两个以上的线程
 * 2、必须是多个线程使用同一个锁
 * 
 * 必须保证同步中只能有一个线程在运行
 * 
 * 好处:解决了多线程的安全问题
 * 弊端:多个线程都需要判断锁,较为消耗资源
 */


class Ticket3 implements Runnable
{
	private int tick=100;
	Object obj=new Object();//创建一个对象
	public void run()
	{
		while(true)
		{
			synchronized(obj)//这个对象“obj”相当于锁,叫同步锁。它有两个标志位0和1,起判断线程
			                 //的作用
			{
				if(tick>0)
				{
					try
					{
						Thread.sleep(10);
					}
					catch(Exception e)
					{
						
					}
					System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
				}
				else
					break; 
			}
		}
	}
}
class TicketDemo3
{
	public static void main(String[] args)
	{
		Ticket3 t=new Ticket3();//这个不是线程
		
		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();
	}
}

/*
* 需求:
* 银行有一个金库
* 有两个储户分别存300元,每次存100,存3次
* 
* 目的:该程序是否有安全问题,如果有如何解决
* 
* 如何找到问题?
* 1、明确哪些代码是多线程运行代码
*    答:run方法中的都是,add方法也是
* 
* 2、明确共享数据
*    答:b和sum
* 
* 3、明确多线程运行代码中,哪些语句是操作共享数据的
*    答:b.add(100);是操作共享数据的,但它只有一句话,您能分开读
*        sum=sum+n;System.out.println("sum="+sum);这两句也是操作共享数据的,
*        所以是这两句
*/
class Bank
{
	private int sum=0;
	public void add(int n)
	{
		sum=sum+n;
		System.out.println("sum="+sum);
	}
}
class Cus implements Runnable
{
	private Bank b=new Bank();
	public void run()
	{
		for(int x=0;x<3;x++)
		{
			b.add(100);
		}
	}
}
class BankDemo
{
	public static void main(String[] args)
	{
		Cus c=new Cus();
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
运行结果为:
sum=100
sum=200
sum=300
sum=400
sum=500
sum=600
 */

//这个程序经过以下检查发现存在安全问题:
class Bank1
{
	private int sum;
	public void add(int n)
	{
		sum=sum+n;
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		System.out.println("sum="+sum);
	}
}
class Cus1 implements Runnable
{
	private Bank1 b=new Bank1();
	public void run()
	{
		for(int x=0;x<3;x++)
		{
			b.add(100);
		}
	}
}
class BankDemo1
{
	public static void main(String[] args)
	{
		Cus1 c=new Cus1();
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
运行结果为:
sum=400
sum=400
sum=600
sum=600
 */


//以上安全隐患,可以加入同步代码块来解决
class Bank2
{
	private int sum;
	Object obj=new Object();
	public void add(int n)
	{
		synchronized(obj)//同步代码块
		{
			sum=sum+n;
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
			
			}
			System.out.println("sum="+sum);
		}
	}
}
class Cus2 implements Runnable
{
	private Bank2 b=new Bank2();
	public void run()
	{
		for(int x=0;x<3;x++)
		{
			b.add(100);
		}
	}
}
class BankDemo2
{
	public static void main(String[] args)
	{
		Cus2 c=new Cus2();
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
运行结果为:
sum=100
sum=200
sum=300
sum=400
sum=500
sum=600
 */

//也可以写为以下同步函数的形式:
class Bank4
{
	private int sum;
	
	public synchronized void add(int n)//也可以写为同步函数
	{
		sum=sum+n;
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		System.out.println("sum="+sum);
	}
}
class Cus4 implements Runnable
{
	private Bank4 b=new Bank4();
	public void run()
	{
		for(int x=0;x<3;x++)
		{
			b.add(100);
		}
	}
}
class BankDemo4
{
	public static void main(String[] args)
	{
		Cus4 c=new Cus4();
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}
}

//卖票程序也可以改为同步函数的形式
class Ticket4 implements Runnable
{
	private int tick=100;
	public synchronized void run()//同步函数,但是这样只有一个进程进去循环,这个线程进去把所有票都打印完了
	                              //也没出来
	{
		while(true)
		{
			if(tick>0)
			{
				try
				{
					Thread.sleep(10);
				}
				catch(Exception e)
				{
						
				}
				System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
			}
			 
		}
	}
}
class TicketDemo4
{
	public static void main(String[] args)
	{
		Ticket4 t=new Ticket4();
		
		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();
	}
}


//以上写法只有一个线程执行了打印票的操作,他都打印完了其他线程也没执行,所以仍是错的,可以写为:
class Ticket5 implements Runnable
{
	private int tick=100;
	public void run()
	{
		while(true)
		{
			 this.show();
		}
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
		}
	}
}
class TicketDemo5
{
	public static void main(String[] args)
	{
		Ticket5 t=new Ticket5();
		
		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();
	}
}
/*
* 同步函数用的是哪一个锁呢?
* 函数需要被对象调用,那么函数都有一个所属对象引用,就是this
* 所以同步函数使用的锁是this
* 
* 通过该函数进行验证
* 为了验证使用两个线程来卖票
* 一个线程在同步代码块中,一个线程在同步函数中,都在执行卖票动作
*/
class Ticket6 implements Runnable
{
	private int tick=100;
	Object obj=new Object();
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...show....:"+tick--);
		}
	}
}
class TicketDemo6
{
	public static void main(String[] args)//这个程序一共有3个线程:主线程、t1、t2
	{
		Ticket6 t=new Ticket6();
		
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);		
		/*
		 * 主线程执行时这个不一定执行,有执行资格但主线程才有执行权,所以可能没等执行它就先执行完了
		 * 下面的语句。解决办法是主程序到t1.start();时先停一下
		 */
		t1.start();
		
		t.flag=false;
		t2.start();
	}
}
//以上程序把主线程执行完才执行了其他线程,所以要让程序停一下,改为:

class Ticket8 implements Runnable
{
	private int tick=100;
	boolean flag=true;
	Object obj=new Object();
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...show....:"+tick--);
		}
	}
}
class TicketDemo8
{
	public static void main(String[] args)
	{
		Ticket8 t=new Ticket8();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		
		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		t.flag=false;
		t2.start();
		
	}
}
/*
 * 以上代码运行时还是出现了安全问题,出现了0票,说明同步的2个前提中至少有一个不满足:
 * 1、必须要有两个或两个以上的线程
 * 2、必须是多个线程使用同一个锁
 * 两个中的一个,不满足哪一个呢?
 * 第一个条件符合,有两个线程,那么就是不满足第二个使用同一个锁,同步代码块的锁是"obj",同步函数的锁是"this"。
 * 所以将同步代码块的锁是"obj"换为"this"即可:
 */
class Ticket11 implements Runnable
{
	private int tick=100;
	boolean flag=true;
	//Object obj=new Object();
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(this)//将同步代码块的锁也改为this,这样就是两个线程在使用同一个锁了
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...show....:"+tick--);
		}
	}
}
class TicketDemo11
{
	public static void main(String[] args)
	{
		Ticket11 t=new Ticket11();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		
		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		t.flag=false;
		t2.start();
		
	}
}

/*
 * 如果同步函数被静态修饰后,使用的锁是什么呢?
 * 通过验证发现又出现了0张票,说明锁不再是this了,因为静态方法中也不可以定义this
 */
class Ticket9 implements Runnable
{
	private static int tick=100;
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(this)
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...show....:"+tick--);
		}
	}
}
class TicketDemo9
{
	public static void main(String[] args)
	{
		Ticket9 t=new Ticket9();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		
		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		t.flag=false;
		t2.start();
		
	}
}



/*
 * 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class 该对象的类型是
 * Class
 * 
 * 静态的同步方法,使用的锁是该方法所在类的字节码文件对象。也就是:类名.class
 */
class Ticket10 implements Runnable
{
	private static int tick=100;
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Ticket10.class)//这里的锁改为"类名.class"
				{
					if(tick>0)
					{
						try
						{
							Thread.sleep(10);
						}
						catch(Exception e)
						{
								
						}
						System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try
			{
				Thread.sleep(10);
			}
			catch(Exception e)
			{
						
			}
			System.out.println(Thread.currentThread().getName()+"...show....:"+tick--);
		}
	}
}
class TicketDemo10
{
	public static void main(String[] args)
	{
		Ticket10 t=new Ticket10();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		
		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		t.flag=false;
		t2.start();
	}
}

/*
* 单例设计模式:
* 
* 饿汉式
*   class Single
*   {
* 		private Single()
* 		{
* 		
* 		}
* 	    //加上final表示s终身指向new Single()这个对象,更为严谨
*       private static final Single s=new Single();
* 		
*       
* 		public static Single getInstance()
* 		{
* 			return s;
* 		}	
*   }
*   
*  
*/

//懒汉式
class Single
{
	//下面这句不能加上final,因为s不能终身为null,否则赋值就赋不了了
	private static Single s=null;
	private Single(){}
	public static Single getInstance()
	{
		if(s==null)
			s=new Single();
		return s;
	}	
}

//如果加入同步代码块可以解决安全问题。但加入同步代码块虽然解决了安全问题但每次都要判断锁比较低效
class Single7
{
	private Single7(){}
	private static Single7 s=null;
	public static synchronized Single7 getInstance()//加入synchronized
	{
		if(s==null)
			s=new Single7();
		return s;
	}
}
class SingleDemo7
{
	public static void main(String[] args)
	{
		Single7 v=Single7.getInstance();
	}
}

//如何做既能解决安全问题又能使程序高效?
class Single8
{
	private Single8(){}
	private static Single8 s=null;
	
	public static Single8 getInstance()
	{
		//③其它线程又进来执行到这儿s不为空了,所以不再执行了
		if(s==null)
		{
			//①线程A、B同时进来了,但进入同步代码块的只能有一个,假设A进到代码块里了,B留在外面了
			//——>B
			synchronized(Single8.class)
			{
				//②A进来后判断s是否为空,是的,所以A往下执行
				if(s==null)
					//——>A,A进入后创建了本类对象
					s=new Single8();
			}
		}
		return s;
	}	
}
class SingleDemo8
{
	public static void main(String[] args)
	{
		Single8 v=Single8.getInstance();
	}
}
/*
 * 面试可能会出的问题:
 * 1、懒汉式和饿汉式有什么不同?
 * 答:懒汉式的特点在于延迟加载
 * 
 * 2、懒汉式的延迟加载有没有问题?怎么解决?
 * 答:有。如果多线程访问时会出现安全问题
 *     可以加同步来解决。用同步代码块或同步函数都行,但是有些低效。用双重判断的形式能解决效率问题
 *     
 * 3、懒汉式加同步时使用的的锁是哪一个?
 * 答:该类所属的字节码文件对象
 * 
 * 以上代码要会写,多写几遍。要求面试的时候如果是笔试题能写出来就行。
 * 让写一个延迟加载的单例设计模式时就写以上懒汉式代码即可
 * 
 * 
 */

/*
 * 死锁:
 * 同步中嵌套同步,而锁却不同
 
class Ticket12 implements Runnable
{
	private static int tick=100;
	Object obj=new Object();
	boolean flag=true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)//Object锁里有this锁
				{
					show();
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()//this锁里有Object锁
	{
		synchronized(obj)
		{
			if(tick>0)
			{
				try
				{
					Thread.sleep(10);
				}
				catch(Exception e)
				{
						
				}
				System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
			}
		}
	}
}
class TicketDemo12
{
	public static void main(String[] args)
	{
		Ticket12 t=new Ticket12();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		
		t1.start();
		try
		{
			Thread.sleep(10);
		}
		catch(Exception e)
		{
			
		}
		t.flag=false;
		t2.start();
	}
}
运行结果没有全部打印出来,运行一会儿没都打印出来就锁住了
*/


//一个死锁程序:
class Test12 implements Runnable
{
	private boolean flag;
	Test12(boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
			synchronized(MyLock.Locka)
			{
				System.out.println("if Locka");
				synchronized(MyLock.Lockb)
				{
					System.out.println("if Lockb");
				}
			}
		}
		else
		{
			synchronized(MyLock.Lockb)
			{
				System.out.println("else Lockb");
				synchronized(MyLock.Locka)
				{
					System.out.println("else Locka");
				}
			}
		}
	}
}
class MyLock
{
	static Object Locka=new Object();
	static Object Lockb=new Object();
}
class DeadLockTest
{
	public static void main(String[] args)
	{
		Thread t1=new Thread(new Test12(true));
		Thread t2=new Thread(new Test12(false));
		
		t1.start();
		t2.start();
	}
}
/*
 * 运行结果为:
 * else lockb
 * if locka
 * 死锁了,只打印这两个。多运行几次有可能会出现4个语句全部打印的情况
 * 
 * 面试题有可能会出让你写一个死锁程序
 */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值