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

目录
  • Java之线程的五大状态及其常用方法(六个状态还有timed_wating超时等待)
    • 1.线程的五大状态及其转换
    • 2.设置或获取多线程的线程名称的方法
    • 3.线程休眠------sleep()方法
    • 4.线程让步------yield()方法
    • 5. 等待线程终止------join()方法
    • 6. 线程停止
    • 7. 线程等待------wait()方法
    • 8. 线程唤醒-------notify()方法
    • 9. notifyAll()方法
  • JAVA多线程有哪几种实现方式?
    • 1. 继承Thread类
    • 2. 实现Runnable接口
    • 3. 使用Callable 和 FutureTask 创建线程
    • 4. 通过线程池创建线程(使用ExecutorService、Callable、Future实现有返回结果的多线程。)
  • 总结

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

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

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

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

(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多线程方法状态和创建内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 总结Java中线程的状态及多线程的实现方式

    线程的状态 线程状态图: 说明: 线程共包括以下5种状态. 1. 新建状态(New) : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread(). 2. 就绪状态(Runnable): 也被称为"可执行状态".线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程.例如,thread.start().处于就绪状态的线程,随时可能被CPU调度执行. 3. 运行状态(Running) : 线程获取CPU权限进行执行.需要注意

  • Java多线程之线程状态的迁移详解

    一.六种状态 java.lang.Thread 的状态分为以下 6 种,它们以枚举的形式,封装在了Thread类内部: NEW:表示线程刚刚创建出来,还未启动 RUNNABLE:可运行状态,该状态的线程可以是ready或running,唯一的决定因素是线程调度器 BLOCKED:阻塞,线程正在等待一个monitor锁以便进入一个同步代码块 WAITING:等待,一种挂起等待的状态.一个线程处于waiting是为了等待其他线程执行某个特定的动作. TIMED_WAITING:定时等待. TERMI

  • Java多线程 线程状态原理详解

    这篇文章主要介绍了Java多线程 线程状态原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java.lang.Thread.State枚举定义了6种线程状态. NEW: 尚未启动(start)的线程的线程状态 RUNNABLE: 运行状态,但线程可能正在JVM中执行,也可能在等待CPU调度 BLOCKED: 线程阻塞,等待监视器锁以进入同步代码块/方法 WAITING: 等待状态.使用以下不带超时的方式时会进入:Object.wait.

  • 学习Java多线程之线程定义、状态和属性

    一 .线程和进程 1. 什么是线程和进程的区别: 线程是指程序在执行过程中,能够执行程序代码的一个执行单元.在java语言中,线程有四种状态:运行 .就绪.挂起和结束. 进程是指一段正在执行的程序.而线程有事也被成为轻量级的进程,他得程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内功空间(代码段.数据段和堆空间)及一些进程级的资源(例如打开的文件),但是各个线程都拥有自己的棧空间. 2. 为何要使用多进程 在操作系统级别上来看主要有以下几个方面: - 使用多线程可以减少程序

  • Java多线程状态及方法实例解析

    这篇文章主要介绍了Java多线程状态及方法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 首先介绍线程的五种状态: 新生态:New Thread() 就绪态:准备抢CPU时间片 运行态:抢到了CPU时间片 阻塞态:放弃已经抢到的CPU时间片,且暂时不参与争抢 死亡态:Run运行完了之后 接下来介绍三种方法:线程的阻塞,线程的优先级设置,线程的礼让 public class MutliThreadDemo4 { public static

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

    目录 Java之线程的五大状态及其常用方法(六个状态还有timed_wating超时等待) 1.线程的五大状态及其转换 2.设置或获取多线程的线程名称的方法 3.线程休眠------sleep()方法 4.线程让步------yield()方法 5. 等待线程终止------join()方法 6. 线程停止 7. 线程等待------wait()方法 8. 线程唤醒-------notify()方法 9. notifyAll()方法 JAVA多线程有哪几种实现方式? 1. 继承Thread类 2

  • Java基础之多线程的三种实现方式

    一.前言 Java多线程实现的三种方式有继承Thread类,实现Runnable接口,使用ExectorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的. 二.继承Thread类实现多线程 1.Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法. 2.start()方法是一个native方法,它将启动一个新线程

  • JAVA基础之注解与反射的使用方法和场景

    注解 注解定义 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. Java 语言中的类.方法.变量.参数和包等都可以被标注.和注释不同,Java 标注可以通过反射获取标注内容.在编译器生成类文件时,标注可以被嵌入到字节码中.Java 虚拟机可以保留标注内容,在运行 时可以获取到标注内容 . 当然它也支持自定义 Java 标注. 注解与注释的区别:注解是给机器看的注释,而注释是给程序员看的提示,编译时自动忽略注释. 使用场景 编译格式检查 反射中解

  • Java基础之详解HashSet的使用方法

    Java HashSet HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合. HashSet 允许有 null 值. HashSet 是无序的,即不会记录插入的顺序. HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的. 您必须在多线程访问时显式同步对 HashSet 的并发访问. HashSet 实现了 Set 接口. HashSet 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类. 添加元素 HashSet

  • Java基础之多线程

    线程中run()和start()的区别: 对于Thread对象来说,当你调用的是start(),线程会被放到等待队列,等待CPU调度,不一定马上执行:无需等待run()方法执行完毕,可以直接执行下面的代码: 而调用的是run()的话,就是当做普通的方法调用,程序还是要顺序执行的: 新建线程的几种方式: 实现Runnable接口:里面实现run()方法: 然后把这个实现了Runnable接口的类就新建为一个Thread t = new Thread(new (实现Runnable接口的类)),调用

  • Java基础高级综合练习题扑克牌的创建

    最近学了很多的知识,脑容量小,记不清,还是得做做练习! 今天就做了一个扑克牌的练习 首先呢..这个逻辑一定要非常清楚,我们要想做出一副扑克牌,必定要弄清楚每一张牌和整的一副牌 首先分析 一张扑克 一张牌里面有什么?相信大家看图(图不是我写的)就应该懂了,一张扑克有属于它自己的花色(红桃,黑桃,梅花,方块) 以及自己的点数(A,2,3-..J,Q,K)就这两种属性,对吧! 那么花色符号,点数符号是个啥? 花色符号就是来代替我们的花色的,我们不可能拿着"红桃"这种文字写进程序吧!所以我们可

  • Java基础教程之HashMap迭代删除使用方法

    前言 map的迭代删除,和我们常见的list,set不太一样,不能直接获取Iteraotr对象,提供的删除方法也是单个的,根据key进行删除,如果我们有个需求,将map中满足某些条件的元素删除掉,要怎么做呢? I. Map 迭代删除 迭代删除,在不考虑并发安全的前提下,我们看下可以怎么支持 1. 非常不优雅版本 我们知道map并不是继承自Collection接口的,HashMap 也没有提供迭代支持,既然没法直接迭代,那我就老老实的low b版好了 Map<String, Integer> m

  • java 多线程的同步几种方法

    java 多线程的同步几种方法 一.引言 前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊.闲话不多说,进入正题. 二.为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常.举个例子,如果一个银行账户同时被两个线程操作,一个取100块,一个存钱100块.假设账户原本有0块,如果取钱线程和存钱线程同时发生,会出现什么结果呢?取钱不成功,账户余额是100.取钱成功了,账户余额是0.那到底是哪个

  • java 基础教程之多线程详解及简单实例

    java 多线程详解 在这篇文章里,我们关注多线程.多线程是一个复杂的话题,包含了很多内容,这篇文章主要关注线程的基本属性.如何创建线程.线程的状态切换以及线程通信. 线程是操作系统运行的基本单位,它被封装在进程中,一个进程可以包含多个线程.即使我们不手动创造线程,进程也会有一个默认的线程在运行. 对于JVM来说,当我们编写一个单线程的程序去运行时,JVM中也是有至少两个线程在运行,一个是我们创建的程序,一个是垃圾回收. 线程基本信息 我们可以通过Thread.currentThread()方法

  • Java基础教程之对象的方法与数据成员

    在Java基础教程之从Hello World到面向对象一文中,我们初步了解了对象(object).对象中的数据成员表示对象的状态.对象可以执行方法,表示特定的动作. 此外,我们还了解了类(class).同一类的对象属于相同的类型(type).我们可以定义类,并使用该定义来产生对象. 我们进一步深入到对象.了解Java中方法与数据成员的一些细节. 调用同一对象的数据成员 方法可以调用该对象的数据成员.比如下面我们给Human类增加一个getHeight()的方法.该方法返回height数据成员的值

随机推荐