黑马程序员-----Java基础学习多线程


----------- android培训java培训、java学习型技术博客、期待与您交流! ------------


/*
进程:是一个正在执行中的程序。(打开任务管理器可初步了解系统进程)
	  每一个进程执行都有一个执行顺序,该循序是一个执行路径,
	  或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。  ----->  多线程就多个独立控制单元
	  线程在控制着进程的执行。                  控制进程的执行。如:迅雷下载多线程时可以提高效率,
	                                                                因多步代码同时运行,可实现多数据连接。


一个进程中至少有一个线程。

Java VM 启动的时候会有一个进程java.exe

该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。


扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制线程。
      
	  注解:说明jvm是多线程执行程序的,如果是单线程执行的话,主线程会
	        一边执行代码,一边处理内存里的垃圾。

1,如何在自定义的代码中,自定义一个线程呢?

通过对api的查找,java已经提供了对线程这类事物的描述。就Thread类。

创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
3,调用线程的start方法。
    该方法两个作用:启动线程,调用run方法。

发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行,(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在相互抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长。cpu说的算。



为什么要覆盖run方法呢?

Thread
*/
class Demo extends Thread
{
	public void run()
	{
		for(int x = 0;x<20;x++)
		{
		System.out.println("Thread run"+x);
		}
	}
}

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

		Demo d = new Demo();//创建好一个线程
		//d.run();//仅仅是对象调用方法,而线程创建了,并没有运行,(所以其实还是单线程)
		d.start();//开启线程并执行该线程的run的方法(其实这个才能开启多线程里的run,d.run是main对对象的调用)
		for(int x = 0; x<20 ; x++)
		{
		System.out.println("Hello World! Hello world"+x);
		}
	}
}


//执行上面代码,观察任务管理器。
//编译javac.exe会执行处理,运行时java.exe会运行处理。



 /* 线程的各种状态:


                                                             具备运行资格
				    --------临时状态(阻塞) 但没有执行权力
                                   |                     |         
                                   |                     |
                                  \|/                   \|/
                 start()                  sleep(time)
        被创建------------->    运行     ------------>   冻结  (还活着,不动而已)  放弃了执行资格
                                  |       <-----------
				  |       sleep()时间到
                       stop();    |       ------------>
		run方法结束	  |          wait()    
                                 \|/      <------------
				消亡		 notify()

  */             

------------------------------------------------------------------------------------------------------------------------------------------------------
/*

需求:简单的卖票程序    
多个窗口卖票。



创建线程的第二种方式:实现Runnable接口

步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
3,通过Thread类建立线程对象
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
   为什么要将Runnable接口的子类对象传递给Thread的构造函数。
   因为,自定义的run方法所属的对象是Runnable接口的子类对象。
   所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。

5,调用Thread 类的start方法开启线程并调用Runnable接口子类的run方法。
 
实现方式和继承方式有什么区别呢?

实现方式的好处:避免了单继承的局限性。

在定义线程时,建立使用实现方式

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


*/

class Ticket implements Runnable// extends Thread
{
	private int tick = 100;
	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(Runnable target)   分配新的 Thread 对象。
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);    

		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();
		//Ticket t4 = new Ticket();

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




	}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
/*
通过分析,发现,打印出1,-1,-2,等错票

过线程的运行出现了安全问题。

问题原因:
	当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分
	,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
	 ------->(其实就是因计算机运行速度快,在条件语句上各线程同时满足,而继续进行了错误的执行)


解决办法:
	对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其它线程不能参与执行。

Java对于多线程的安全问题提供了专业的解决方式

就是同步代码块。

synchronized (对象)
{
	需要被同步的代码
}
对象如同有锁。持有锁的线程可以再同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁

通俗例子:火车上的卫生间

同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,比较为消耗资源。

同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this

通过该程序进行验证。

使用两个线程来买票。
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。


如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不在是this,因为静态方法中也不可以定义this

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象
类名.class 该对象的类型是Class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class




*/



class Ticket implements Runnable
{
	private int tick = 100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"....sale:"+ tick--);
				}
			}
		}
	}
}

class  TicketDemo2
{
	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);
		Thread t4 = new Thread(t);

	}
}






---------------------------------------------------------------------------------------------------------------------------------------------------------

/*
练习:
创建两个线程,和主线程交替运行

原来线程都有自己默认的名称。
Thread-编号  该编号从0开始

static Thread.currentThread():获取当前线程对象。
getName(); 获取线程名称

设置线程名称:setName或者构造函数。

*/


class Test extends Thread
{
	//private String name;
	Test(String name)
	{
		//this.name = name;
		super(name);
	}
	public void run()
	{
		for(int x = 0 ; x<60 ; x++)
		{
			//System.out.println(this.getName()+" run....."+x);
			System.out.println((Thread.currentThread()==this)+" run....."+this.getName()+x);

			//currentThread()  返回对当前正在执行的线程对象的引用。
			//getName()  返回该线程的名称。

		}
	}
}


class ThreadTest
{
	public static void main(String[] args) 
	{
		//System.out.println("Hello World!");
		Test t1 = new Test("new");
		Test t2 = new Test("tow_tow_tow");
		t1.start();
		t2.start();

		for(int x = 0;x<60; x++)
		{
			System.out.println("main_____"+x);
		}
	}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------
/*
单例设计模式

*/


//饿汗式。

/*
class SingleHungry
{
	private static final SingleHungry s = new SingleHungry();
	private  SingleHungry(){}
	public static SingleHungry getInstance() //返回类型是一个类
	{
		return s;
	}
	public static void print()
	{
		for(int x = 0;x<10; x++)
		{
			System.out.println("SingleHungry");
		}
	}
}
*/


//懒汉式
class Single
{
	private static Single s = null;
	private Single(){}

	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)
			{
				if(s==null)
					//----->A;
					s = new Single();

			}
		}
		return s;
	}
}

class  SingleDemo
{
	public static void main(String[] args) 
	{
		//System.out.println("Hello World!");
        SingleHungry.getInstance().print();
	}
}

--------------------------------------------------------------------------------------------------------------------------------------------------------



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值