黑马程序员--------java 多线程

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


多线程


一:概述
1、进程:是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做控制单元。
2、线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。
3、一个进程至少有一个线程。
4、JVM在启动时会有一个进程java.exe。该线程中至少一个线程负责java程序运行,而这个线程运行的代码存在于main方法中,该线程称为主线程
5、扩展:其实更细节说明JVM,jvm启动不止一个线程,还有负责垃圾回收机制的线程

二、创建线程
创建新执行线程有两种方法。
1、一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法(将自定义代码存储在run方法中),在调用线程start()方法。该方法有两个作用:启动线程和调用run方法。(如果调用的是run方法,仅仅是对象的调用。线程创建了但是并没有开启)
2、实现Runnable接口。
1)定义实现Runnable接口
2)覆盖Runnable接口中的run方法()
3)通过Thread类建立线程对象
4)将Runable接口的子类对象作为实际参数传递给Thread类的构造函数
5)调用Thread类的start方法,开启线程,并调用Runnable接口子类的run方法
3、实现方式和继承方式有什么区别呢?
1)实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式
2)继承Thread:线程代码存放Thread子类run方法中
实现Runnable,线程代码存在接口子类的run方法中


三、线程运行状态

四:获取线程对象及名称
1、线程都有自己默认的名称:Thread-编号,该编号从0开始
2、名称可以初始化父类构造方法(Thread(String name)),super(name);
3、Thread.currentThread();获取当前线程对象
getName():获取线程名称
设置线程名称:setName或者构造函数初始化

五:多线程安全问题
1、当多条语句在操作同一个共享数据时,一条线程对多条语句只执行了一部分,没有执行完,另一条线程参与进来执行。导致共享数据的错误
2、解决方法:对多条操作操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其它线程不可以参与执行
3、java对多线程安全问题提供了专业的解决方法——就是同步代码块:
synchronized(对象)(对象如同锁,持有锁得线程可以在同步中执行)
{
需要被同步的代码;(看哪些代码在操作共享数据)
}
4、同步的前提:
1)必须有两个或者两个以上的线程
2)必须是多个线程使用同一个锁
5、如何明确代码需要同步:
1)明确哪些代码是多线程运行代码
2)明确共享数据
3)明确多线程运行代码中哪些语句是操作共享数据的
6、同步的好处是解决多线程安全问题;弊端是多个线程需要判断锁,较为消耗资源
7、同步函数:使用的锁是this
8、静态同步函数:用于的锁是 类名.class 。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象——类名.class 该类型的对象是Class
9:死锁:

示例:买票程序

/*

多个窗口买票程序
*/
class Ticket implements Runnable
{
	int ticket = 4000;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
					if(ticket>0)
				{
					try
					{
						Thread.sleep(5);
					}
					catch (Throwable e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"....."+ticket--);
				}	
			}

			
		}
	}
}
class  TicketDemo
{
	public static void main(String[] args) 
	{
		Ticket t =new Ticket();
		Thread t1 = new Thread(t).start();
		Thread t2 = new Thread(t).start();
		Thread t3 = new Thread(t).start();
		Thread t4 = new Thread(t).start();
		
	}
}

示例解释:单例懒汉式同步知识点
懒汉式代码

<span style="font-size:14px;">class Single
{
	private static Single s = null;
	private Single(){}//将构造函数私有化,其它类不能对其建立对象
	public static Single getInstance()//对外提供访问接口
	{
		if(s==null)
		{
			synchronized (Single.class)//静态同步函数的锁是其字节码文件对象
			{
				if(s==null)//加上双重判断解决多线程等待后重复new对象
					s = new Single;
			}
		}		
		return s;
	}
}
</span>
1、汉式和饿汉式有什么不同:懒汉式的特点是实例的延迟加载
2、懒汉式延迟加载有没有问题? :有,如果多线程访问时容易出现安全问题
3、怎么解决:可以加同步来解决,用同步函数和同步代码块都行,但是稍微有些低效。用双重判断和同步代码块可以解决这个问题
4、加同步的时候使用的锁是哪一个:该类所属的字节码文件对象——Single.class

死锁程序(要掌握)

/*
线程间死锁
同步中嵌套同步,而锁却不同
*/
class SiSuo implements Runnable
{
	private boolean flag;
	SiSuo(Boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Lock.locka)
				{
					System.out.println("if locka" );
					synchronized(Lock.lockb)
					{
						System.out.println("if lockb" );
					}
						
				}
			}
			
		}
		else
		{		
			while(true)
			{
				synchronized(Lock.lockb)
				{
					System.out.println("else locka" );
					synchronized(Lock.locka)
					{
						System.out.println("else lockb" );
					}
						
				}
			}
			
		}
	}
}
class Lock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}
class SiSuoDemo
{
	public static void main(String[] args) 
	{
		Thread t1 = new Thread(new SiSuo(true));
		Thread t2 = new Thread(new SiSuo(false));
		t1.start();
		t2.start();
	}
}


六线程间通信

1、待唤醒机制:

2、wait()、notify()、notifyAll()都使用在同步中。因为要对持有监视器(锁)的线程操作,所以要使用在同步中

3、等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object中

示例:

/*
多线程 生产者,消费者
生产一个消费一个

*/
class Resouce
{
	private String name;
	private int count=1;
	private boolean flag=false;
	public synchronized void set(String name)//使用同步函数,同步锁是this
	{
		
		while(flag)//循环判断标记,为真就等待,如果此处使用if会造成生产2个使用一个的情况。当t1、t2线程都在此等待时
			try{this.wait();}catch(Exception e){}//下一次被唤醒并获得执行权时,会直接执行下面语句。所以要用while循环
		this.name=name+"...."+count++;
		System.out.println(Thread.currentThread().getName()+"....生产者...."+this.name);
		flag=true;//执行完一次后将标记反向
		this.notifyAll();//唤醒所有的等待线程

	}
	public synchronized void get()//同步函数
	{
		while(!flag)//循环判断标记,非真就等待,此处使用if会造成生产一个使用2个的情况。当t3、t4线程都在此等待时
			try{this.wait();}catch(Exception e){}//下一次被唤醒的时候不会判断if语句,而是直接执行下面语句
		System.out.println(Thread.currentThread().getName()+"....消费者................"+this.name);
		flag=false;
		this.notifyAll();//唤醒等待线程
	}
}
class Produser implements Runnable
{
	private Resouce r;//创建一个类类型引用变量
	Produser(Resouce r)
	{
		this.r=r;
	}
	public void run()//调用线程执行run方法
	{
		while(true)
		{
			r.set("商品");
		}

	}
	
}
class Consumer implements Runnable
{
	private Resouce r;
	Consumer(Resouce r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.get();
		}
	}
}
class ProduserConsumerDemo
{
	public static void main(String[] args) 
	{
		Resouce r = new Resouce();
		Produser p =new Produser(r);
		Produser p1=new Produser(r);
		Consumer c = new Consumer(r);
		Consumer c1 = new Consumer(r);
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p1);
		Thread t3 = new Thread(c);
		Thread t4 = new Thread(c1);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

七 线程停止
1、如何停止线程:使run方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
2、特殊情况:当线程处于冻结状态,就不会读取标记,那么下次就不会结束
解决方式:(调用interrupt方法)当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
线程停止程序示例:

/*
1、如何停止线程:使run方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
2、特殊情况:当线程处于冻结状态,run方法里面有wait等,就不会读取标记,那么下次就不会结束
   解决方式:(调用interrupt方法)当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

*/
class StopThread implements Runnable
{
	private boolean flag = true;
	public synchronized void run()
	{
		while(flag)
		{
			try
			{
				wait();//线程等待,并释放执行权
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread().getName()+"......Exception");//打印当前异常的线程名
				flag = false;
			}
			System.out.println(Thread.currentThread().getName()+"......run");
		}
	}
	public void change()
	{
		flag = false;
	}
}
class StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		//t1.Daemon(true)//将t1 t2设置为守护线程,即后台线程,当前台线程如主线程运行结束
		//t2.Daemon(true)//后,后台线程也结束运行Java虚拟机退出。
		t1.start();
		t2.start();
		
		int x = 0;
		while(true)
		{
			if(x++==60)
			{
				//st.change();//改变线程标记来停止线程。在一种特殊情况下这两个线程也不会结束,
				              //就是当run方法中加上同步时,这时就不能采取这种方式让线程停止了
				t1.interrupt();//当run方法中加上同步,且线程处于等待状态时,就必须使用interrupt方法
				               //来对冻结的线程进行唤醒,再改吧循环标记,使线程停止
				t2.interrupt();
				break;
			}	
			System.out.println(Thread.currentThread().getName()+"......run");
		}

		System.out.println("end");
	}
}

八 守护线程

setDaemon(boolean on)

  当线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用
九、Join方法
1:、当A线程执行到了B线程的 .Join()方法时,A就会等待,等B线程都执行完,A才会执行。
2、Join可以用来临时加入线程执行
/*
join方法
当线程A执行到线程B的join方法时,A就会等待。等B线程都执行完,A才会执行
join可以用来临时加入线程执行
*/
class Join implements Runnable
{
	public void run()
	{
		for(int x =0;x<60;x++)
		{
			System.out.println(Thread.currentThread().getName()+"....."+x);
		}
	}
}
class JoinDemo 
{
	public static void main(String[] args)throws InterruptedException 
	{
		Join j = new Join();
		Thread t1 = new Thread(j);
		Thread t2 = new Thread(j);
		t1.start();
		//t1.join();//当t1调用了join方法,就会抢夺主线程执行权,主线程进入等待状态,t1执行完后主线程恢复向下执行,这时t2与主线程交替运行
		t2.start();
		t1.join();//当t2在t2线程后面时,t1、t2交替运行,t1执行完后,t2就与主线程交替运行
		for(int y = 0;y<70;y++)
		{
			System.out.println(Thread.currentThread().getName()+"...."+y);
		}
		System.out.println("end");
	}
}

十 优先级 & yield 方法

yield暂停当前正在执行的线程对象,并执行其他线程。


开发多线程一般写法示例:

/*
开发时多线程一般写法

采用匿名内部类的写法
*/
class ThreadTest 
{
	public static void main(String[] args) 
	{
		new Thread()
		{
			public void run()
			{
				for(int x =0;x<2000;x++)
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		}.start();
		
		for(int x =0;x<2000;x++)
		{
			System.out.println(Thread.currentThread().getName()+"...."+x);
		}
		Runnable r = new Runnable()
		{
			public void run()
			{
				for(int x =0;x<2000;x++)
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		};
		new Thread(r).start();
		
	}
}




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







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值