Java——Thread线程类与Runnable接口

Java语言中实现多线程的方法有两种:一种是继承java.lang包中的Thread类;另一个是用户在定义自己的类中实现Runnable接口。。但不管采用哪种方法,都要用到Java语言库中的Thread类以及相关方法。

一、利用Thread类的子类和Runnable接口来创建线程

Java语言的基本类型中已经定义了Thread这个基本类,内置了一组方法,是程序利用该类提供的方法产生一个新的线程、执行一个线程。终止一个线程,或是查看线程的执行状态。

继承Thread类是实现线程的一种方法。Thread类的构造方法如下所示:

Thread类的构造方法

构造方法功能说明
public Thread()创建一个线程对象,此线程对象的名称是“Thread-n”的形式,其中n是一个整数。使用这个构造方法,必须创建Thread类的一个子类并覆盖其run()方法
public Thread(String name)创建一个线程对象,参数name指定了线程的名称
public Thread(Runnable target)创建一个线程对象,此线程对象的名称是“Thread*n”的形式,其中n是一个整数。参数target的run()方法将被线程对象调用,作为其执行代码
public Thread(Runnable target,String name)功能同上,参数target的run()方法将被线程对象调用,作为其执行代码。参数name指定了新创建线程的名称

Thread类的常用方法

常用方法功能说明
public static Thread currentThread()返回当前正在执行的线程对象
public final String getName()返回线程的名称
public void start()是该线程由新建状态变为就绪状态。如果该线程已经是就绪状态,则产生IllegalStateException异常
public void run()线程应执行任务
public final boolean isAlive当线程处于就绪状态或执行状态时,给该线程设置中断标志;一个正在执行的线程让睡眠线程调用该方法,则可导致睡眠线程发生InterruptedException异常
public void interruption()判断该线程是否处于中断,若是返回true,否则返回false
public final void join()暂停当前线程的执行,等待调用该方法的线程结束后再继续执行本程序
public final int getPriority()返回线程的优先级
public final void setPriority(int newPriority)设置线程优先级。如果当前线程不能修改这个线程,则产生SecurityException异常。如果参数不在所要求的优先级内,则产生IlleagalArgumentException异常
public static void yield()暂停当前线程的执行,但该线程仍处于就绪状态,不转为阻塞状态。该方法只给同优先级线程以执行的机会
public static void sleep(long millis)当前执行的线程指定睡眠时间。参数millis是线程睡眠的毫秒数。如果这个线程已经被别的线程中断,则产生InterruptedException异常

要在一个Thread的子类里激活线程,必须先做好下列两件事。
(1)此类必须是继承自Thread类
(2)线程所要执行的代码必须是写在run()方法内

package 创建Thread类的子类来创建线程
class MyThread extends Thread
{
	private String who;
	public MyThread(String name)
	{
		who=name;
	}
	public void run()
	{
		for(int i=0;i<5;i++)
		{
			try
				{sleep((int)(1000-Math.random());}
			catch(InterruptedException e){}
			System.out.println(who+"正在运行!!!");
		}
	}
}

public class 示例
{
	public static void main(String[] args)
	{
		MyThread t1=new MyThread("你");
		MyThread t2=new MyThread("我");
		t1.start();
		t2.start();
		System.out.println("主方法main()运行结束")
	}
}
运行结果:
主方法main()运行已结束
你正在运行!!
她正在运行!!
你正在运行!!
她正在运行!!
你正在运行!!
你正在运行!!
她正在运行!!
她正在运行!!
你正在运行!!
她正在运行!!
package 利用Runnable接口创建线程;

class MyThread implements Runnable
{
	private String who;
	public MyThread(String str)
	{
		who = str;
	}
	public void run()
	{
		for(int i =0;i<5;i++)
		{
			try
			{
				Thread.sleep((int)(1000*Math.random()));
			}
			catch(InterruptedException e) {}
			System.out.println(who+"正在运行!!");
		}
	}
}
public class jl1102 {
	public static void main(String[] args)
	{
		MyThread you = new MyThread("你");
		MyThread she = new MyThread("他");
		Thread t1 = new Thread(you);
		Thread t2 = new Thread(she);
//		Thread t1 = new MyThread("你"); error!
		t1.start();
		t2.start();
		System.out.println("主方法main()运行已结束");
	}
}
运行结果:
主方法main()运行已结束
你正在运行!!
你正在运行!!
他正在运行!!
你正在运行!!
他正在运行!!
你正在运行!!
你正在运行!!
他正在运行!!
他正在运行!!
他正在运行!!

预期 System.out.println(“主方法main()运行已结束”); 是最后执行的,为什么第一行就输出了呢?事实上,main()方法本身也是一个线程,因此执行完 t1.start();t2.start(); 会继续向下执行,接着输出 主方法main()运行已结束 。 至于是先执行谁一般是看谁先抢到cpu资源而定。通常是 System.out.println(“主方法main()运行已结束”); 语句,因为它不用经过线程激活过程。

下面进行修改,让 System.out.println(“主方法main()运行已结束”); 最后执行;

package jl1103在多线程程序中join方法的使用;

class MyThread extends Thread
{
	private String who;
	public MyThread(String str)
	{
		who = str;
	}
	public void run()
	{
		for(int i=0;i<5;i++)
		{
			try
			{
				sleep((int)(1000*Math.random()));
			}
			catch(InterruptedException e) {}
			System.out.println(who+"正在运行!!");	
		}
	}
}
public class jl1103 {
	public static void main(String[] args)
	{
		MyThread you = new MyThread("你");
		MyThread she = new MyThread("他");
		you.start();
		try {
			you.join();//限制you结束后才往下执行
		}
		catch(InterruptedException e) {}
		she.start();
		try {
			she.join();
		}
		catch(InterruptedException e) {}
		System.out.println("主方法main()运行结束!");
	}
}
运行结果:
你正在运行!!
你正在运行!!
你正在运行!!
你正在运行!!
你正在运行!!
他正在运行!!
他正在运行!!
他正在运行!!
他正在运行!!
他正在运行!!
主方法main()运行结束!
二、线程间的数据共享

同一进程的多个线程间可以共享相同的内存单元,并利用这些共享单元来实现数据交换。实时通信和必要的同步操作。对于利用构造方法Thread(Runnable target)这种方式创建的线程,当轮到它来相用cpu资源时,可运行对象的成员变量自然就是这些线程共享的数据单元。

package 线程间的数据共享;
class ThreadSale extends Thread
{
	private int tickets=10;
	public void run() {
		while(true)
		{
			if(tickets>0)
				System.out.println(this.getName()+"  售机票第"+tickets--+"号");
			else
				System.exit(0);
		}
	}
}
public class jl1104 {
	public static void main(String[] args)
	{
		ThreadSale t1 = new ThreadSale();
		ThreadSale t2 = new ThreadSale();
		ThreadSale t3 = new ThreadSale();
		t1.start();
		t2.start();
		t3.start();
	}
}
运行结果:
Thread-0  售机票第10号
Thread-0  售机票第9号
Thread-0  售机票第8号
Thread-1  售机票第10号
Thread-2  售机票第10号
Thread-1  售机票第9号
Thread-1  售机票第8号
Thread-1  售机票第7号
Thread-1  售机票第6号
Thread-0  售机票第7号
Thread-1  售机票第5号
Thread-1  售机票第4号
Thread-1  售机票第3号
Thread-2  售机票第9号
Thread-1  售机票第2号
Thread-0  售机票第6号
Thread-1  售机票第1号
Thread-2  售机票第8号
Thread-0  售机票第5号
Thread-0  售机票第4

可看出Thread类不能实现数据间的共享,下面看Runnable接口:

package jl1105线程间的数据共享runneable;

class ThreadSale implements Runnable
{
	private int tickets=10;
	public void run() {
		while(true)
		{
			if(tickets>0)
				System.out.println(Thread.currentThread().getName()+"  售机票第"+tickets--+"号");
			else
				System.exit(0);
		}
	}
}
public class jl1105 {
	public static void main(String[] args)
	{
		ThreadSale t=new ThreadSale();
		Thread t1=new Thread(t,"第1售票窗口");
		//用同一可运行对象t作为参数创建3个线程,第二个参数为线程名
		Thread t2=new Thread(t,"第2售票窗口");
		Thread t3=new Thread(t,"第2售票窗口");
		t1.start();
		t2.start();
		t3.start();
	}
}
运行结果:
第1售票窗口  售机票第10号
第1售票窗口  售机票第9号
第1售票窗口  售机票第8号
第1售票窗口  售机票第7号
第1售票窗口  售机票第6号
第1售票窗口  售机票第5号
第1售票窗口  售机票第4号
第1售票窗口  售机票第3号
第1售票窗口  售机票第2号
第1售票窗口  售机票第1

其实用Thread将 private int tickets=10; 设置为静态也可以实现共享内存,如下

Thread-2  售机票第10号
Thread-1  售机票第9号
Thread-0  售机票第8号
Thread-0  售机票第5号
Thread-0  售机票第4号
Thread-1  售机票第6号
Thread-1  售机票第2号
Thread-1  售机票第1号
Thread-2  售机票第7号
Thread-0  售机票第3

Runnable接口用了t作为共享类

三、多线程的同步控制

同步跟共享数据是由去区别的,共享是指线程之间对内存数据共享,因为线程共同拥有对内存空间中数据的处理权力,这样会导致因为多个线程同时处理数据而是数据出现不一样,所以提出了同步解决此问题,即同步是在共享的基础之上,是针对多个多线程共享会导致数据不一致而提出来的。

package jl1106多线程的同步控制;
class Mbank
{
	private static int sun=2000;
	public static void take(int k)
	{
		int temp=sun;
		temp-=k;
		try {
			Thread.sleep((int)(1000*Math.random()));
		}
		catch(InterruptedException e) {}
		sun = temp;
		System.out.println("sum="+sun);
	}
}
class Customer extends Thread
{
	public void run()
	{
		for(int i =1;i<=4;i++)
			Mbank.take(100);
	}
}
public class jl1106 {
	public static void  main(String[] args)
	{
		Customer c1=new Customer();
		Customer c2=new Customer();
		c1.start();
		c2.start();
	}
}
运行结果:
sum=1900
sum=1800
sum=1900
sum=1700
sum=1800
sum=1700
sum=1600
sum=1600

为什么会这样呢? 因为在线程相互抢夺时,数据还没有保存,所以会造成上面的情况,下面给出解决方案。

package jl1107多线程的同步控制1;
class Mbank
{
	private static int sum=2000;
	public synchronized static void take(int k)
	{
		int temp=sum;
		temp-=k;
		try {
			Thread.sleep((int)(1000*Math.random()));
		}
		catch(InterruptedException e)
		{
			
		}
		sum=temp;
		System.out.println("sum="+sum);
	}
}
class Customer extends Thread
{
	public void run()
	{
		for(int i=1;i<5;i++)
			Mbank.take(100);
	}
}
public class jl1107 {
	public static void main(String[] args)
	{
		Customer c1=new Customer();
		Customer c2=new Customer();
		c1.start();
		c2.start();
	}
}
运行结果:
sum=1900
sum=1800
sum=1700
sum=1600
sum=1500
sum=1400
sum=1300
sum=1200

synchronized的功能:首先判断对象或方法的互斥锁是否在,若在就获得互斥锁,然后就可以执行紧随其后的临界代码或方法体;如果对象或是方法的互斥锁不在(已被其他线程拿走),就进入等待状态,知道获得互斥锁。当被synchrnized限定的代码段执行完,就自动释放互斥锁。

四、线程之间的通讯

多线程的执行往往需要相互之间的配合。为了更加有效地协调不同线程的工作,需要在线程间建立沟通渠道,通过线程间的“对话”来解决线程间的同步问题,而不仅仅是依靠互斥机制。例如,当一个人在排队买面包时,若她给销售员的不是零钱,而售卖员又没有零钱找给她,那她就必须等待,并允许她后面的人先买,一遍售卖员获得零钱找给她。如果她后面的这个人仍没有零钱,那么她俩都必须等待,并允许后面的人先买。

java.lang,Object类的wait()、notify()和notifyAll()等方法为线程间的通讯提供了有效手段。下表给出了Object类中用于线程间通讯的常用方法

Object类中用于线程间通讯的常用方法

常用方法功能说明
public final void wait()如果一个正在执行同步代码(synchronized)的线程A执行了wait()调用(在对象x上),该线程暂停执行而进入对象x的等待队列,并释放已获得的对象x的互斥锁。线程A要一直等到其他线程在对象x上调用notify()或notifyAll()方法,才能够在重新获得对象x的互斥锁后继续执行(从wait()语句后继续执行)
public void nofity()唤醒正在等待该对象互斥锁的第一个线程
public void notifyAll()唤醒正在等待该对象的所有线程,具有最高优先级的线程首先被唤醒并执行

下面通过一些例子说明wait()、notify()方法的应用

//用俩个线程模拟存票、售票过程,但要求没存入一张票,就售出一张票,在存入,直到售完为止。
class Tickets
{
	protected int size;
	int number=0;
	boolean available=false;
	public Tickets(int size)
	{
		this.size=size;
	}
	public synchronized void put()
	{
		if(available)
			try {
				wait();
			}
		catch(Exception e) {}
		System.out.println("存入【"+(++number)+"】号票");
		available=true;
		notify();
	}
	public synchronized void sell() {
		if(!available)
			try{wait();}
			catch(Exception e) {}
		System.out.println("售出【"+number+"】号票");
		available=false;
		notify();
		if(number==size)
			number=size+1;
	}
}
class Producer extends Thread
{
	Tickets t =null;
	public Producer(Tickets t)
	{
		this.t=t;
	}
	public void run() {
		while(t.number<t.size)
			t.put();
	}
}
class Consumer extends Thread
{
	Tickets t =null;
	public Consumer(Tickets t)
	{
		this.t=t;
	}
	public void run() {
		while(t.number<t.size)
			t.sell();
	}
}
public class jl1108 {
	public static void main(String[] args)
	{
		{
			Tickets t=new Tickets(10);
			new Producer(t).start();
			new Consumer(t).start();
		}
	}
}
运行结果:
存入【1】号票
售出【1】号票
存入【2】号票
售出【2】号票
存入【3】号票
售出【3】号票
存入【4】号票
售出【4】号票
存入【5】号票
售出【5】号票
存入【6】号票
售出【6】号票
存入【7】号票
售出【7】号票
存入【8】号票
售出【8】号票
存入【9】号票
售出【9】号票
存入【10】号票
售出【10】号票
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值