服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java基础之多线程方法状态和创建方法

Java基础之多线程方法状态和创建方法

2021-12-18 14:41无极的移动代码 Java教程

Java中可以通过Thread类和Runnable接口来创建多个线程,下面这篇文章主要给大家介绍了关于Java基础之多线程方法状态和创建方法的相关资料,需要的朋友可以参考下

Java之线程的五大状态及其常用方法(六个状态还有timed_wating超时等待)

1.线程的五大状态及其转换

线程的五大状态分别为:创建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead)。

下面画出线程五大状态之间的关系图:

Java基础之多线程方法状态和创建方法

(1)新建状态:即单纯地创建一个线程,创建线程有三种方式,在我的博客:线程的创建,可以自行查看!

(2)就绪状态:在创建了线程之后,调用Thread类的start()方法来启动一个线程,即表示线程进入就绪状态!

(3)运行状态:当线程获得CPU时间,线程才从就绪状态进入到运行状态!

(4)阻塞状态:线程进入运行状态后,可能由于多种原因让线程进入阻塞状态,如:调用sleep()方法让线程睡眠,调用wait()方法让线程等待,调用join()方法、suspend()方法(它现已被弃用!)以及阻塞式IO方法。

(5)死亡状态:run()方法的正常退出就让线程进入到死亡状态,还有当一个异常未被捕获而终止了run()方法的执行也将进入到死亡状态!

2.设置或获取多线程的线程名称的方法

由于在一个进程中可能有多个线程,而多线程的运行状态又是不确定的,即不知道在多线程中当前执行的线程是哪个线程,所以在多线程操作中需要有一个明确的标识符标识出当前线程对象的信息,这个信息往往通过线程的名称来描述。在Thread类中提供了一些设置或获取线程名称的方法:

(1)创建线程时设置线程名称:

public Thread(Runnable target,String name)

(2)设置线程名称的普通方法:

public final synchronized void setName(String name)

(3)取得线程名称的普通方法:

public final String getName()

演示:

class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<5;i++)
		{
			//currentThread()方法用于取得当前正在JVM中运行的线程
			//使用getName()方法,用于获取线程的名称
			System.out.println("当前线程:"+Thread.currentThread().getName()+"-----i="+i);
		}
	}
}
public class Test1 {
	public static void main(String[] args){
		//创建线程对象thread1且没有设置线程名称
		MyThread myThread1=new MyThread();
		Thread thread1=new Thread(myThread1);
		thread1.start();
		//创建线程对象thread2且使用setName设置线程名称
		MyThread myThread2=new MyThread();
		Thread thread2=new Thread(myThread2);
		thread2.setName("线程2");
		thread2.start();
		//创建线程对象thread3并在创建线程时设置线程名称
		MyThread myThread3=new MyThread();
		Thread thread3=new Thread(myThread3,"线程3");
		thread3.start();
	}
}

输出:

当前线程:Thread-0-----i=0
当前线程:Thread-0-----i=1
当前线程:Thread-0-----i=2
当前线程:线程3-----i=0
当前线程:线程2-----i=0
当前线程:线程2-----i=1
当前线程:线程2-----i=2
当前线程:线程2-----i=3
当前线程:线程3-----i=1
当前线程:Thread-0-----i=3
当前线程:Thread-0-----i=4
当前线程:线程3-----i=2
当前线程:线程3-----i=3
当前线程:线程3-----i=4
当前线程:线程2-----i=4

通过上述代码及其运行结果可知:

(1)若没有手动设置线程名称时,会自动分配一个线程的名称,如线程对象thread1自动分配线程名称为Thread-0。

(2)多线程的运行状态是不确定的,不知道下一个要执行的是哪个线程,这是因为CPU以不确定方式或以随机的时间调用线程中的run()方法。

(3)需要注意的是,由于设置线程名称是为了区分当前正在执行的线程是哪一个线程,所以在设置线程名称时应避免重复!

3.线程休眠------sleep()方法

线程休眠:指的是让线程暂缓执行,等到预计时间之后再恢复执行。

(1)线程休眠会交出CPU,让CPU去执行其他的任务。

(2)调用sleep()方法让线程进入休眠状态后,sleep()方法并不会释放锁,即当前线程持有某个对象锁时,即使调用sleep()方法其他线程也无法访问这个对象。

(3)调用sleep()方法让线程从运行状态转换为阻塞状态;sleep()方法调用结束后,线程从阻塞状态转换为可执行状态。

sleep()方法:

public static native void sleep(long millis) throws InterruptedException;

从上面方法参数中可以看出sleep()方法的休眠时间是以毫秒作为单位。

关于sleep()方法的操作如下:

class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<5;i++)
		{
			//使用Thread类的sleep()方法,让线程处于休眠状态
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("当前线程:"+Thread.currentThread().getName()+"-----i="+i);
		}
	}
}
public class Test1 {
	public static void main(String[] args){
		MyThread myThread=new MyThread();
		//利用myThread对象分别创建三个线程
		Thread thread1=new Thread(myThread);
		thread1.start();
		Thread thread2=new Thread(myThread);
		thread2.start();
		Thread thread3=new Thread(myThread);
		thread3.start();
	}
}

某次运行结果如下所示:

当前线程:Thread-2-----i=0
当前线程:Thread-1-----i=0
当前线程:Thread-0-----i=0
当前线程:Thread-2-----i=1
当前线程:Thread-0-----i=1
当前线程:Thread-1-----i=1
当前线程:Thread-2-----i=2
当前线程:Thread-1-----i=2
当前线程:Thread-0-----i=2
当前线程:Thread-2-----i=3
当前线程:Thread-1-----i=3
当前线程:Thread-0-----i=3
当前线程:Thread-2-----i=4
当前线程:Thread-0-----i=4
当前线程:Thread-1-----i=4

注:

(1)通过运行代码进行观察,发现运行结果会等待一段时间,这就是sleep()方法让原本处于运行状态的线程进入了休眠,从而进程的状态从运行状态转换为阻塞状态。

(2)以上代码创建的三个线程肉眼观察,发现它们好像是同时进入休眠状态,但其实并不是同时休眠的。

4.线程让步------yield()方法

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

(1)调用yield()方法让当前线程交出CPU权限,让CPU去执行其他线程。

(2)yield()方法和sleep()方法类似,不会释放锁,但yield()方法不能控制具体交出CPU的时间。

(3)yield()方法只能让拥有相同优先级的线程获取CPU执行的机会。

(4)使用yield()方法不会让线程进入阻塞状态,而是让线程从运行状态转换为就绪状态,只需要等待重新获取CPU执行的机会。

yield()方法:

public static native void yield();

关于yield()方法的操作如下:

class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<5;i++)
		{
			//使用Thread类的yield()方法
			Thread.yield();
			System.out.println("当前线程:"+Thread.currentThread().getName()+"-----i="+i);
		}
	}
}
public class Test1 {
	public static void main(String[] args){
		MyThread myThread=new MyThread();
		//利用myThread对象分别创建三个线程
		Thread thread1=new Thread(myThread);
		thread1.start();
		Thread thread2=new Thread(myThread);
		thread2.start();
		Thread thread3=new Thread(myThread);
		thread3.start();
	}
}

某次运行结果如下所示:

当前线程:Thread-0-----i=0
当前线程:Thread-0-----i=1
当前线程:Thread-0-----i=2
当前线程:Thread-0-----i=3
当前线程:Thread-0-----i=4
当前线程:Thread-1-----i=0
当前线程:Thread-2-----i=0
当前线程:Thread-2-----i=1
当前线程:Thread-2-----i=2
当前线程:Thread-2-----i=3
当前线程:Thread-2-----i=4
当前线程:Thread-1-----i=1
当前线程:Thread-1-----i=2
当前线程:Thread-1-----i=3
当前线程:Thread-1-----i=4

5. 等待线程终止------join()方法

等待线程终止:指的是如果在主线程中调用该方法时就会让主线程休眠,让调用join()方法的线程先执行完毕后再开始执行主线程。

join()方法:

public final void join() throws InterruptedException {
      join(0);
  }

注:上面的join()方法是不带参数的,但join()方法还可以带参数,下去自行了解!

关于join()方法的操作如下:

class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<2;i++)
		{
			//使用Thread类的sleep()方法
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("当前线程:"+Thread.currentThread().getName()+"-----i="+i);
		}
	}
}
public class Test1 {
	public static void main(String[] args) throws InterruptedException{
		MyThread myThread=new MyThread();
		Thread thread1=new Thread(myThread,"自己创建的线程");
		thread1.start();
		System.out.println("主线程:"+Thread.currentThread().getName());
		//线程对象thread1调用join()方法
		thread1.join();
		System.out.println("代码结束");
	}
}

运行结果如下所示:

主线程:main
当前线程:自己创建的线程-----i=0
当前线程:自己创建的线程-----i=1
代码结束

若不调用join()方法的话,运行结果如下所示:

主线程:main
代码结束
当前线程:自己创建的线程-----i=0
当前线程:自己创建的线程-----i=1

故通过两个运行结果可以更加深刻地感受到调用join()方法后的作用!调用join()方法和不调用join()方法的区别!

6. 线程停止

多线程中停止线程有三种方式:

(1)设置标记位,让线程正常停止。

class MyThread implements Runnable{
	//设置标记位
	private boolean flag=true;
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {
		int i=0;
		while(flag)
		{
			System.out.println("第"+(i++)+"次执行-----"+"线程名称:"+Thread.currentThread().getName());
		}
	}
}
public class Test1 {
	public static void main(String[] args) throws InterruptedException{
		MyThread myThread=new MyThread();
		Thread thread1=new Thread(myThread,"自己创建的线程");
		thread1.start();
		//让主线程sleep一毫秒
		Thread.sleep(1);
		//修改标记位的值,让自己创建的线程停止
		myThread.setFlag(false);
		System.out.println("代码结束");
	}
}

运行结果如下所示:

第0次执行-----线程名称:自己创建的线程
第1次执行-----线程名称:自己创建的线程
第2次执行-----线程名称:自己创建的线程
第3次执行-----线程名称:自己创建的线程
第4次执行-----线程名称:自己创建的线程
第5次执行-----线程名称:自己创..............
第19次执行-----线程名称:自己创建的线程
第20次执行-----线程名称:自己创建的线程
第21次执行-----线程名称:自己创建的线程
第22次执行-----线程名称:自己创建的线程
第23次执行-----线程名称:自己创建的线程
第24次执行-----线程名称:自己创建的线程
第25次执行-----线程名称:自己创建的线程
第26次执行-----线程名称:自己创建的线程
代码结束

(2)使用stop()方法强制使线程退出,但是使用该方法不安全,已经被废弃了!

class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<100;i++)
		{
			System.out.println("线程名称:"+Thread.currentThread().getName()+"------i="+i);
		}
	}
}
public class Test1 {
	public static void main(String[] args) throws InterruptedException{
		MyThread myThread=new MyThread();
		Thread thread1=new Thread(myThread,"自己创建的线程");
		thread1.start();
		//让主线程sleep一毫秒
		Thread.sleep(1);
		//调用已被弃用的stop()方法去强制让线程退出
		thread1.stop();
		System.out.println("代码结束");
	}
}

某次运行结果如下所示:

线程名称:自己创建的线程------i=0
线程名称:自己创建的线程------i=1
线程名称:自己创建的线程------i=2
线程名称:自己创建的线程------i=3
线程名称:自己创建的线程------i=4
线程名称:自己创建的线程------i=5
线程名称:自己创建的线程------i=6
......
线程名称:自己创建的线程------i=28
线程名称:自己创建的线程------i=29
线程名称:自己创建的线程------i=30
线程名称:自己创建的线程------i=31
线程名称:自己创建的线程------i=48
线程名称:自己创建的线程------i=49
线程名称:自己创建的线程------i=50
线程名称:自己创建的线程------i=51线程名称:自己创建的线程------i=51代码结束

从上述代码和运行结果可以看出,原本线程对象thread1的run()方法中应该执行100次语句“System.out.println(“线程名称:”+Thread.currentThread().getName()+"------i="+i);”,但现在没有执行够100次,所以说stop()方法起到了让线程终止的作用。再从运行结果上可以看出,i=51被执行了两次且没有换行,这就体现了调用stop()方法的不安全性!

下面正式地解释stop()方法为什么不安全?

因为stop()方法会解除由线程获得的所有锁,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程会立即停止,假如一个线程正在执行同步方法:

public synchronized void fun(){
	x=3;
	y=4;
}

由于方法是同步的,多线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x=3;时,被调用的stop()方法使得线程即使在同步方法中也要停止,这就造成了数据的不完整性。故,stop()方法不安全,已经被废弃,不建议使用!

(3)使用Thread类的interrupt()方法中断线程。

class MyThread implements Runnable{
	@Override
	public void run() {
		int i=0;
		while(true)
		{
			//使用sleep()方法,使得线程由运行状态转换为阻塞状态
			try {
				Thread.sleep(1000);
				//调用isInterrupted()方法,用于判断当前线程是否被中断
				boolean bool=Thread.currentThread().isInterrupted();
				if(bool) {
					System.out.println("非阻塞状态下执行该操作,当前线程被中断!");
					break;
				}
				System.out.println("第"+(i++)+"次执行"+" 线程名称:"+Thread.currentThread().getName());
			} catch (InterruptedException e) {
				System.out.println("退出了!");
				//这里退出了阻塞状态,且中断标志bool被系统自动清除设置为false,所以此处的bool为false
				boolean bool=Thread.currentThread().isInterrupted();
				System.out.println(bool);
				//退出run()方法,中断进程
				return;
			}
		}
	}
}
public class Test1 {
	public static void main(String[] args) throws InterruptedException{
		MyThread myThread=new MyThread();
		Thread thread1=new Thread(myThread,"自己创建的线程");
		thread1.start();
		//让主线程sleep三秒
		Thread.sleep(3000);
		//调用interrupt()方法
		thread1.interrupt();
		 System.out.println("代码结束");
	}
}

运行结果如下所示 :

第0次执行 线程名称:自己创建的线程
第1次执行 线程名称:自己创建的线程
第2次执行 线程名称:自己创建的线程
代码结束
退出了!
false

(1)interrupt()方法只是改变中断状态而已,它不会中断一个正在运行的线程。具体来说就是,调用interrupt()方法只会给线程设置一个为true的中断标志,而设置之后,则根据线程当前状态进行不同的后续操作。

(2)如果线程的当前状态出于非阻塞状态,那么仅仅将线程的中断标志设置为true而已;

(3)如果线程的当前状态出于阻塞状态,那么将在中断标志设置为true后,还会出现wait()、sleep()、join()方法之一引起的阻塞,那么会将线程的中断标志位重新设置为false,并抛出一个InterruptedException异常。

(4)如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞状态的情况来进行处理。例如,一个线程在运行状态时,其中断标志设置为true之后,一旦线程调用了wait()、sleep()、join()方法中的一种,立马抛出一个InterruptedException异常,且中断标志被程序自动清除,重新设置为false。

总结:调用Thread类的interrupted()方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted()方法真正实现线程的中断原理是 :开发人员根据中断标志的具体值来决定如何退出线程。

7. 线程等待------wait()方法

首先,wait()方法是Object类的方法,下面是无参的wait()方法:

public final void wait() throws InterruptedException {
      wait(0);
  }

(1)wait()方法的作用是让当前正在执行的线程进入线程阻塞状态的等待状态,该方法时用来将当前线程置入“预执行队列”中,并且调用wait()方法后,该线程在wait()方法所在的代码处停止执行,直到接到一些通知或被中断为止。

(2)wait()方法只能在同步代码块或同步方法中调用,故如果调用wait()方法时没有持有适当的锁时,就会抛出异常。

(3)wait()方法执行后,当前线程释放锁并且与其他线程相互竞争重新获得锁。

下面调用wait()方法:

public class Test1 {
	public static void main(String[] args) {
		Object object=new Object();
		synchronized (object) {
			System.out.println("调用wait()前");
			//调用Object类的wait()方法
			try {
				object.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("调用wait()后");
		}
	} 
}

运行结果如下所示:

调用wait()前

解析:此时,程序依然处于执行状态。原本应该打印两条语句:调用wait()前 调用wait()后,但是由于该程序还没有运行完而且只打印了一条语句:调用wait()前。这是因为什么呢?因为调用了Object类的wait()方法,使得程序在执行wait()方法之后一直等待下去,故只执行了调用wait()方法前的语句。但程序不能这样一直等待下去,这个时候就需要另一个方法去唤醒调用wait()方法的处于等待状态的线程,让等待线程继续执行下去,该方法为notify()方法。

8. 线程唤醒-------notify()方法

首先,notify()方法也是Object类的方法:

public final native void notify();

(1)notify()方法要在同步代码块或同步方法中调用。

(2)notify()方法是用来通知那些等待该对象的对象锁的线程,对其调用wait()方法的对象发出通知让这些线程不再等待,继续执行。

(3)如果有多个线程都在等待,则由线程规划器随机挑选出一个呈wait状态的线程将其线程唤醒,继续执行该线程。

(4)调用notify()方法后,当前线程并不会马上释放该对象锁,要等到执行notify()方法的线程执行完才会释放对象锁。

下面调用notify()方法:

class MyThread implements Runnable{
	private boolean flag;
	private Object object;
	//定义一个构造方法
	public MyThread(boolean flag,Object object) {
		this.flag=flag;
		this.object=object;
	}
	//定义一个普通方法,其中调用了wait()方法
	public void waitThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用wait()前------"+Thread.currentThread().getName());
				//调用wait()方法
				this.object.wait();
				System.out.println("调用wait()后------"+Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//定义一个普通方法,其中调用了notify()方法
	public void notifyThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用notify前------"+Thread.currentThread().getName());
				//调用notify()方法
				this.object.notify();
				System.out.println("调用notify()后------"+Thread.currentThread().getName());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	@Override
	public void run() {
		if(this.flag) {
			this.waitThread();
		}else {
			this.notifyThread();
		}
	}	
}
public class Test1 {
	public static void main(String[] args) {
		Object object=new Object();
		//实例化调用wait()的线程
		MyThread wait=new MyThread(true,object);
		Thread waitThread=new Thread(wait,"wait线程");
		//实例化调用notify()的线程
		MyThread notify=new MyThread(false,object);
		Thread notifyThread=new Thread(notify,"notify线程");
		//启动线程
		waitThread.start();
		//调用一下sleep()方法,使得查看效果更明显
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		notifyThread.start();
	} 
}

运行结果

调用wait()前------wait线程
调用notify前------notify线程
调用notify()后------notify线程
调用wait()后------wait线程

解析:根据run方法及实例化的线程对象,wait线程执行了waitThread()方法,该方法中调用了wait()方法使得线程进入线程阻塞状态的等待状态并释放锁,如果没有其他线程去唤醒该线程的话wait线程将一直等待下去。此时,notify线程执行了notifyThread()方法,该方法中调用了notify()方法,使得notify线程去唤醒wait线程,让wait线程不再等待下去,并且先将notify线程执行完后释放锁再执行wait线程的wait()方法之后的语句。所以打印如上所示。

但要注意的是,当有多个线程处于等待时,调用notify()方法唤醒线程时,就会依然有线程处于等待状态,演示如下:

class MyThread implements Runnable{
	private boolean flag;
	private Object object;
	//定义一个构造方法
	public MyThread(boolean flag,Object object) {
		this.flag=flag;
		this.object=object;
	}
	//定义一个普通方法,其中调用了wait()方法
	public void waitThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用wait()前------"+Thread.currentThread().getName());
				//调用wait()方法
				this.object.wait();
				System.out.println("调用wait()后------"+Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//定义一个普通方法,其中调用了notify()方法
	public void notifyThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用notify前------"+Thread.currentThread().getName());
				//调用notify()方法
				this.object.notify();
				System.out.println("调用notify()后------"+Thread.currentThread().getName());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	@Override
	public void run() {
		if(this.flag) {
			this.waitThread();
		}else {
			this.notifyThread();
		}
	}	
}
public class Test1 {
	public static void main(String[] args) {
		Object object=new Object();
		//实例化调用wait()的线程
		MyThread wait=new MyThread(true,object);
		Thread waitThread1=new Thread(wait,"wait线程1");
		Thread waitThread2=new Thread(wait,"wait线程2");
		Thread waitThread3=new Thread(wait,"wait线程3");
		//实例化调用notify()的线程
		MyThread notify=new MyThread(false,object);
		Thread notifyThread=new Thread(notify,"notify线程");
		//启动3个等待线程
		waitThread1.start();
		waitThread2.start();
		waitThread3.start();
		//调用一下sleep()方法,使得查看效果更明显
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		notifyThread.start();
	} 
}

运行结果如下所示:

调用wait()前------wait线程1
调用wait()前------wait线程3
调用wait()前------wait线程2
调用notify前------notify线程
调用notify()后------notify线程
调用wait()后------wait线程1

此时程序并没有执行完毕,因为依然有线程处于等待状态,所以notify()只是随机将某一个等待线程唤醒,并没有唤醒所有等待线程。那么,若有多个线程处于等待状态时,如何将所有等待线程都唤醒呢?下面将介绍notifyAll()方法

9. notifyAll()方法

Object类的notifyAll()方法:

public final native void notifyAll();

notifyAll()方法将同一对象锁的所有等待线程全部唤醒。代码演示如下:

class MyThread implements Runnable{
	private boolean flag;
	private Object object;
	//定义一个构造方法
	public MyThread(boolean flag,Object object) {
		this.flag=flag;
		this.object=object;
	}
	//定义一个普通方法,其中调用了wait()方法
	public void waitThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用wait()前------"+Thread.currentThread().getName());
				//调用wait()方法
				this.object.wait();
				System.out.println("调用wait()后------"+Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	//定义一个普通方法,其中调用了notifyAll()方法
	public void notifyThread() {
		synchronized (this.object) {
			try {
				System.out.println("调用notify前------"+Thread.currentThread().getName());
				//调用notifyAll()方法
				this.object.notifyAll();
				System.out.println("调用notify()后------"+Thread.currentThread().getName());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	@Override
	public void run() {
		if(this.flag) {
			this.waitThread();
		}else {
			this.notifyThread();
		}
	}	
}
public class Test1 {
	public static void main(String[] args) {
		Object object=new Object();
		//实例化调用wait()的线程
		MyThread wait=new MyThread(true,object);
		Thread waitThread1=new Thread(wait,"wait线程1");
		Thread waitThread2=new Thread(wait,"wait线程2");
		Thread waitThread3=new Thread(wait,"wait线程3");
		//实例化调用notifyAll()的线程
		MyThread notify=new MyThread(false,object);
		Thread notifyThread=new Thread(notify,"notify线程");
		//启动3个等待线程
		waitThread1.start();
		waitThread2.start();
		waitThread3.start();
		//调用一下sleep()方法,使得查看效果更明显
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		notifyThread.start();
	} 
}

运行结果如下所示:

调用wait()前------wait线程1
调用wait()前------wait线程2
调用wait()前------wait线程3
调用notify前------notify线程
调用notify()后------notify线程
调用wait()后------wait线程3
调用wait()后------wait线程2
调用wait()后------wait线程1

解析:此时,程序执行完毕,所有等待线程都被调用notifyAll()方法的具有同一对象锁的线程唤醒,故每一个等待线程都会在调用wait()后继续执行直到该线程结束。

 

JAVA多线程有哪几种实现方式?

1. 继承Thread类

1)定义Thread类的子类,并重写Thread类的run()方法。

2)创建Thread子类的实例,及创建了线程对象。

3)调用线程对象的start()方法来启动该线程。

class MyThread extends Thread{
	public void run(){
		System.out.println("线程运行");
	}
}
public class Test{
	public static void main(String[] args){
		MyThread thread=new MyThread();
		thread.start();//开启线程
	}
}

Thread类常用方法

Thread.currentThread():是Thread类的静态方法,该方法总是返回当前正在执行的线程对象。

String getName():该方法是Thread类的实例方法,是返回调用该方法的线程名字。

2. 实现Runnable接口

1)定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法同样是线程执行体。

2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

3)调用线程对象的start()方法来启动该线程。

class Runna implements Runnable
{
	public void run(){
		System.out.println("线程运行");
	}
}
public class Test{
	public static void main(String[] args){
		Runna runna=new Runna();
		Thread t=new Thread(runna);
		t.start();//开启线程
	}
}

推荐使用那种:很明显,我们在使用继承Thread方式时,有一个很大的缺点,就是我们继承了Thread后就不能继承其他类了。但是如果我们实现Runnable的话,恰好可以解决这个问题。

  • 一个类只能extends一个父类,但可以implements多个接口。
  • 一个接口则可以同时extends多个接口,却不能implements任何接口。

3. 使用Callable 和 FutureTask 创建线程

1)创建Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值 。

2)创建Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象, 该 FutrueTask 对象封装了该 Callable 对象的 call() 方法的返回值。

3)使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

4)调用FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

public class ThreadDemo03 {

  /**
   * @param args
   */
  public static void main(String[] args) {
      // TODO Auto-generated method stub

      Callable<Object> oneCallable = new Tickets<Object>();
      FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);

      Thread t = new Thread(oneTask);

      System.out.println(Thread.currentThread().getName());

      t.start();

  }

}

class Tickets<Object> implements Callable<Object>{

  //重写call方法
  @Override
  public Object call() throws Exception {
      // TODO Auto-generated method stub
      System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
      return null;
  }   
}

4. 通过线程池创建线程(使用ExecutorService、Callable、Future实现有返回结果的多线程。)

ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值就很方便了。

通过分析可以知道,他同样也是实现了Callable接口,实现了Call方法,所以有返回值。这也就是正好符合了前面所说的两种分类

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。

public class ThreadDemo05{

  private static int POOL_NUM = 10;     //线程池数量

  /**
   * @param args
   * @throws InterruptedException 
   */
  public static void main(String[] args) throws InterruptedException {
      // TODO Auto-generated method stub
      ExecutorService executorService = Executors.newFixedThreadPool(5);  
      for(int i = 0; i<POOL_NUM; i++)  
      {  
          RunnableThread thread = new RunnableThread();

          //Thread.sleep(1000);
          executorService.execute(thread);  
      }
      //关闭线程池
      executorService.shutdown(); 
  }   

}

class RunnableThread implements Runnable  
{     
  @Override
  public void run()  
  {  
      System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");  

  }  
}  

程序运行结果:

通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-4
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-5
通过线程池方式创建的线程:pool-1-thread-2
通过线程池方式创建的线程:pool-1-thread-5
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-4
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-2

 

总结

到此这篇关于Java基础之多线程方法状态和创建方法的文章就介绍到这了,更多相关Java多线程方法状态和创建内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_41270824/article/details/120070126

延伸 · 阅读

精彩推荐
  • Java教程mybatis mybatis-plus-generator+clickhouse自动生成代码案例详解

    mybatis mybatis-plus-generator+clickhouse自动生成代码案例详解

    这篇文章主要介绍了mybatis mybatis-plus-generator+clickhouse自动生成代码案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要...

    Max&Min8062021-12-10
  • Java教程java 代理机制的实例详解

    java 代理机制的实例详解

    这篇文章主要介绍了java 代理机制的实例详解的相关资料,这里说明下如何实现代理机制,帮助大家理解掌握这部分内容,需要的朋友可以参考下...

    sheungxin2532020-12-20
  • Java教程Java回调方法详解

    Java回调方法详解

    本篇文章主要介绍了Java回调方法的步骤、回调的例子、异步回调与同步回调、回调方法在通信中的应用等。具有一定的参考价值,下面跟着小编一起来看下...

    byhieg3162020-07-22
  • Java教程Java创建删除文件和目录的方法(推荐)

    Java创建删除文件和目录的方法(推荐)

    这篇文章主要介绍了java创建删除文件和目录的方法,创建和删除文件目录常用的是file类的方法,具体内容详情大家参考下本文...

    liruli8812021-04-25
  • Java教程Mybatis实现自定义的typehandler三步曲

    Mybatis实现自定义的typehandler三步曲

    这篇文章主要介绍了Mybatis实现自定义的typehandler三步曲的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 ...

    liuzhiyong05243242020-05-31
  • Java教程初步认识JVM的体系结构

    初步认识JVM的体系结构

    大家都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制、编译过程和如何对Java程序进行调优相信都会有一个很好的认知.在面试中JVM也是非常重要的一部...

    weixin_574855425072021-09-18
  • Java教程Java遍历集合的三种方式

    Java遍历集合的三种方式

    本文主要对于遍历集合获取其对象,总结的三种简单的方式进行介绍,文章中举了两个案例进行对比,具有很好的参考价值,下面就跟小编一起来看下吧...

    Bigerf5362020-07-14
  • Java教程浅析JAVA中的内存结构、重载、this与继承

    浅析JAVA中的内存结构、重载、this与继承

    这篇文章主要介绍了 JAVA中的内存结构、重载、this与继承的相关资料,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...

    521125LYC4392021-08-24