Java多线程的实现

一、进程和线程的概念

进程:正在执行的应用程序,是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源。多进程提高了CPU的资源使用率,但多进程并不是并发执行的,而是在很小的时间片内切换进程执行。

线程:进程的执行单元,执行路径。多线程提高了应用程序的使用率,由于Java的线程调度是抢占式调度,在没有设置优先级的情况下,多个线程随机的占用应用程序执行。

二、多线程的实现方式

  • 继承Thread类

1.自定义类MyThread继承Thread类

2.MyThread类里面重写run()

不是所有类中的代码都需要被多线程执行的,为了区分哪些代码能够别线程执行,Java提供了Thread类中的run()用来包含那些被线程执行的代码。

3.创建对象

获取线程对象名

Public final String getName();获取线程名称

设置线程对象名

Public final void setName(String name);设置线程名称

MyThread mt=new MyThread("张建辉");构造方法内设置

4.启动线程

Run()仅仅是封装被线程执行的代码,直接调用是普通方法

Start()首先启动了线程对象,然后再由jvm调用该线程的run() 方法

 模拟窗口卖票

public class SellTicket extends Thread{
	//定义票在run方法外,static使得类在加载的时候只初始化 一次,保证多个对象共享100张票
	private static  int tickets=100;
	@Override
	public void run() {
		
		while(true){
			//实际中票是有延迟的,而且如果不加睡眠线程票数的变化不是顺序的。
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(tickets>0){
				//继承Thread类,所以可以直接使用getName()方法
				System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
			}
		}		
	}	
}
public class SellTicketDemo {
	public static void main(String[] args) {
		//创建资源对象
		SellTicket s1=new SellTicket();
		SellTicket s2=new SellTicket();
		SellTicket s3=new SellTicket();
		//创建线程对象
		s1.setName("窗口1");
		s2.setName("窗口2");
		s3.setName("窗口3");
		
		s1.start();
		s2.start();
		s3.start();		
	}	
}
  • 实现Runnable接口

1.自定义类MyRunnable实现Runnable接口

2.重写run()方法:run()里面封装了被线程执行的代码

3.创建MyRunnable()类的对象

4.创建Thread类的对象,并把3步骤的对象作为构造参数传递

 模拟窗口卖票

public class SellTickets implements Runnable {
	private int tickets=100;
	//创建锁对象
	private Object obj=new Object();
	@Override
	public void run() {
		while(true){
			//同步代码块,括号中相当于锁的功能,实现够共享100张票。
			synchronized(obj){	//发现这里代码将来是会被锁上的,所以t1进来后,就锁了。
				if(tickets>0){
					try {
						Thread.sleep(100);//t1睡眠了
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					//由于实现的是Runnable接口,所以间接的用Thread类的getName()方法
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
				}
			}
			//t1出来后锁就开了
		}
	}

}
public class SellTicketsDemo {
	public static void main(String[] args) {
		SellTickets s=new SellTickets();
		
		Thread t1=new Thread(s,"窗口1");
		Thread t2=new Thread(s,"窗口2");
		Thread t3=new Thread(s,"窗口3");
		
		t1.start();
		t2.start();
		t3.start();
	}
}
  • 两种方式的比较

1.方式2可以避免由于Java单继承带来的局限性(可以多实现)

2.方式2适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

三、同步与锁的问题

我们知道多个线程是抢占式的占用程序资源,如果一个线程进入方法后,一旦被其他线程抢占,其他线程就能够同样执行同一个方法,但是如果上一个线程还没有结束,而方法中又包含有共享数据,就会出现线程安全问题。如果有一种工具可以使一个线程使用共享数据,只有这个线程结束使用共享数据之后,其他线程才能够使用,这就是Java提供的同步和锁。

Synchronized关键字

A:同步代码块:同步可以解决安全问题的根本原因就是在对象上,对象如同锁的功能一样,多线程必须是同一把锁。
synchronized(对象) {
需要被同步的代码;

}

这里的锁对象可以是任意对象。

B:同步方法:把同步加在方法上。

synchronized(this) {
需要被同步的代码;

}

这里的锁对象是this

C:静态同步方法

synchronized(类.class) {
需要被同步的代码;

}

这里的锁对象是当前类的字节码文件对象

Lock接口(使用ReenTranLock实现类)

       Voidlock()加锁

       Voidunlock()释放锁

直接在所要加锁的代码前后使用lock 和unlock方法

 四、线程组、线程池

线程组ThreadGroup是由多个线程组成的集合,ThreadGroup类提供了一些方法,可以对组内的所有线程执行相同的操作统一调用,而不需对单个线程单独写方法。

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

五、线程的生命周期

新建 -- 就绪 -- 运行 -- 死亡

新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡



猜你喜欢

转载自blog.csdn.net/tigaoban/article/details/72763674