多线程的前世今生

本文详细介绍了多线程的概念,包括进程与线程的区别,以及如何在Java中创建和管理线程。重点讨论了线程安全、同步机制、线程间通信以及如何优雅地停止线程,对于多线程编程提供了实用的指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、多线程概述

1、进程:操作系统中同时运行的程序

是一个正在执行的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径。
或者叫一个控制单元。
每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。

2、线程:在同一个进程中同时运行的任务

就是进程中的一个独立的(执行任务)控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。多个线程可以共享数据。如:多线程下载软件
Java VM启动的时候会有一个进程java.exe。
该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。
该线程称为主线程

3、扩展

其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

4、线程与进程的比较

线程具有许多传统进程所具有的特征,故又称为轻型线程(Light-Weight Process)或进程元;而把传统的进程称为重型进程(Heavy-Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少需要一个线程。

进程与线程的区别:

1)进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。

2)线程的堆空间是共享的,栈空间是独立的,现场称消耗的资源也比进程小,相互之间可以影响的。

二、创建线程

1、如何在自定义的代码中,自定义一个线程呢?

通过对API的查找,java已经提供䠾对线程的这类事物的描述,就
Thread类,创建线程的第一种方式,继承Thread类。
步骤:
a.定义继承Thread。
b.复写Thread类中的run方法。
目的:将自定义代码存储在run方法,让线程运行。
c.调用线程的start方法,该方法有两个作用:启动线程和调用run方法。

2、随机性

当多线程程序运行时,发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就执行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象地把多线程的运行行为视为在互相抢夺cpu的执行权。
这就是多线程的特性:随机性。谁抢到谁执行,至于执行多长,cpu说了算。

3、为什么要覆盖run方法呢?

Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。
例1
class Demo extends Thread//继承线程,覆盖run方法
{
	public void run()
	{
		for (int x=0;x<60 ;x++ )//创建个循环打印看效果
		{
			System.out.println("demo run-----"+x);
		}
	}
}

class ThreadDemo
{
	public static void main(String[] args)
	{
		Demo d=new Demo();//创建好一个线程
		//d.start();//开启线程并执行该线程的run方法
		d.run();//仅仅是对象调用方法,而线程创建了,并没有运行。

		for (int x=0;x<60 ;x++ )//创建个循环打印看效果
		{
			System.out.println("Hello world---"+x);
		}
	}
}
小练习:
/*
需求:创建两个线程和主程序交替运行
思路:1.创建一个类继承线程,
	  2.主函数中开启两条线程
*/

class Test extends Thread//Test继承线程
{
	private String name;
	Test(String name)//创建名字方法比较好分别出来
	{
		this.name=name;
	}
	public void run()//覆盖父类run方法
	{
		for (int x=0;x<60 ;x++ )
		{
			System.out.println(name+"run---"+x);
		}
		
	}
}

class TestDemo
{
	public static void main(String[] args)
	{
		Test t1=new Test("one");//创建第一条线程
		Test t2=new Test("two");//创建第二条线程
		t1.start();//开启线程,运行run方法
		t2.start();//开启线程,运行run方法
		
		for (int x=0;x<60 ;x++ )
		{
			System.out.println("main"+x);
		}
		
	}
}
结果输出:

4、线程运行状态

5、获取线程对象及名称

线程都有自己的名称
Thread-编号 该编号从0开始。
局部的变量在每一个线程中都有一份。
statiic Thread current Thread( );获取当前线程对象。
getName();获取线程名称
设置线程名称:setName或者构造函数。

6、创建线程的第二种方式

实现从Runnable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
       为什么要将Runnable接口的子类对象传递给Thread的构造函数。
        因为,自定义的run方法所属的对象时Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
例1
思路:1.定义类实现接口Runnable
	  2.存放要运行的代码在run方法中,覆盖Runnable中的run方法
	  3.在主函数中建立线程对象
	  4.将Runable的子类对象作为实际参数传递给Thread
	  5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
*/

class Ticket implements Runnable//实现Runnable接口
{
	private int tick=100;
	public void run()//存放要运行的代码在run方法中,覆盖Runnable中的run方法
	{
		while(true)
		{
			if (tick>0)
			{
				System.out.println(Thread.currentThread().getName()+"ticket---"+tick--);//获取当前线程名和票数自减
			}
		}
	}
}

class TicketDemo
{
	public static void main(String[] args)
	{
		Ticket t=new Ticket();//创建Runnable的子类对象

		Thread t1 =new Thread(t);//开启线程并将Runable的子类对象作为实际参数传递给Thread
		Thread t2 =new Thread(t);
		Thread t3 =new Thread(t);
		Thread t4 =new Thread(t);
		t1.start();//调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
		t2.start();
		t3.start();
		t4.start();
	}
}
结果输出:

 

7、多线程的安全问题

通过对上例分析,发现有可能打印出0,-1,-2等错票。
多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。
里一个线程就参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,在让一个线程都执行完之前,
在执行过程中,其他线程不可以参加执行。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码(找共享数据)
}
例2
/*
需求:简单的卖票程序
	多个窗口同时卖票
思路:1.定义类实现接口Runnable
	  2.存放要运行的代码在run方法中,覆盖Runnable中的run方法
	  3.在主函数中建立线程对象
	  4.将Runable的子类对象作为实际参数传递给Thread
	  5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
*/

class Ticket implements Runnable//实现Runnable接口
{
	private int tick=100;
	Object obj=new Object();
	public void run()//存放要运行的代码在run方法中,覆盖Runnable中的run方法
	{
		while(true)
		{
			synchronized(obj)//同步代码块
			{
				if (tick>0)
				{
					try
					{
						Thread.sleep(10);//中断异常
					}
					catch (Exception e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"ticket---"+tick--);//获取当前线程名和票数自减
				}
			}
		}
	}
}

class TicketDemo
{
	public static void main(String[] args)
	{
		Ticket t=new Ticket();//创建Runnable的子类对象

		Thread t1 =new Thread(t);//开启线程并将Runable的子类对象作为实际参数传递给Thread
		Thread t2 =new Thread(t);
		Thread t3 =new Thread(t);
		Thread t4 =new Thread(t);
		t1.start();//调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
		t2.start();
		t3.start();
		t4.start();
	}
}

8、多线程同步代码块

对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去。因为没有锁
例:同一节火车上的卫生间
同步的前提:
a.必须要有两个或者两个以上的线程
b.必须是多个线程使用同一个锁。
必须保证同步中只有一个线程在运行
好处:解决了多线程的安全问题。
弊端:多个线程都需要判断锁,较为消耗资源。

9、同步函数

格式:在函数上加synchronized即可
例3
/*
需求:银行有一个金库
有两个储户分别存300,每次存100,存3次。

目的:该程序是否有安全问题,如果有,如何解决?

如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
	
*/

class Bank
{
	private int sum;
	Object obj=new Object();
	public synchronized void add(int n)//找到问题后,同步函数
	{
		sum=sum+n;
		try
		{
			Thread.sleep(10);//中断异常,需要对其进行处理
		}
		catch (Exception e)
		{
		}
		System.out.println("sum="+sum);
	}
}

class Cus implements Runnable//类实现Runnable
{
	private Bank b=new Bank();
	public void run()
	{
		for (int x=0;x<3 ;x++ )//累加循环
		{
			b.add(100);
		}
	}
}

class BankDemo
{
	public static void main(String[] args)
	{	
		Cus c=new Cus();
		Thread t1=new Thread(c);//创建线程
		Thread t2=new Thread(c);
		t1.start();//启动线程
		t2.start();
	}
}
结果输出:

 
a.同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
所以同步函数使用的锁是this。

通过程序进行验证。

使用两个线程来买票。
一个线程在同步代码块中。
一个线程在同步函数中。
都在执行买票动作。


结果证明:若同步代码块锁的对象仍是obj,则运行时它们是交替运行,但是会出现安全问题。
当把锁的的对象改为this时,能解决安全问题。
b.如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中不可以定义this。
静态进内存中,内存中没有本类对象,但是一定有该类对应的字节码文件对象(锁)。
类名.class 该对象的类型是class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

例4
//加同步的单例设计模式——懒汉式 
//一般多用饿汉式,因为懒汉式存在安全隐患
class Single
{
	private Single s=null;
	private Single(){}
	
	public static Single getInstance()
	{
		if (s==null)//双重判断,提高效率
		{
			synchronized(Single.class)//静态的同步方法锁为该类对应的字节码文件对象
			{
				if (s==nul)
				{
					s=new Single();
				}
			}
		}
		return s
	}
}

10、死锁

当同步中嵌套同步时,就有可能出现死锁现象。如:两个人各有一根筷子,想夹菜得需要另一根。
相互借就处于和谐状态,不借就死磕,谁也吃不到。
例5
class Test implements Runnable//实现Runnable接口
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag=flag;
	}

	public void run()
	{
		if (flag)
		{
			while(true)
			{
				synchronized(MyLock.locka)//a锁
				{
					System.out.println("if locka");
					synchronized(MyLock.lockb)//b锁
					{
						System.out.println("if lockb");
					}
				}
			}
		}

		else
		{
			synchronized(MyLock.lockb)//b锁
			{
				System.out.println("else lockb");
				synchronized(MyLock.locka)//a锁
				{
					System.out.println("else locka");
				}
			}
		}
	}
}

class MyLock//创建个类来装两个锁
{
	static Object locka=new Object();
	static Object lockb=new Object();
}

class DeadLockTest
{
	public static void main(String[] args)
	{
		Thread t1=new Thread(new Test(true));//创建线程
		Thread t2=new Thread(new Test(false));
		t1.start();//开启线程
		t2.start();
	}
}
结果输出:

三、线程间的通信

1、使用同步操作同一资源

多线程操作同一资源,但线程间的运行代码不一致
例1
/*
线程间通讯:
其实就是多个线程在操作同一个资源,
但是操作的动作不同。
*/

class Res//操作的资源
{
	String name;
	String sex;
}

class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{
			synchronized(r)//用唯一的资源作锁
			{	
				if(x==0)//操作同一资源
				{
					r.name="Mike";
					r.sex="man";     
				}
				else
				{
					r.name="丽丽";
					r.sex="女女女女女";
				}
				x=(x+1)%2;//创造0,1互换的循环体
			}
		}
	}
}

class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while (true)
		{
			synchronized(r)//用唯一的资源作锁
			{
				System.out.println(r.name+"----"+r.sex);//操作同一资源
			}
		}
	}
}

class InputOutputDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();//创建资源对象

		Input in=new Input(r);//操作同一资源
		Output out=new Output(r);

		Thread t1=new Thread(in);//与线程相关联
		Thread t2=new Thread(out);
		t1.start();
		t2.start();
	}
}
结果输出:

2、等待唤醒机制

wait();
notify();
notifyAll();

都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。

为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
例2
class Res//操作的资源
{
	String name;
	String sex;
	boolean flag=false;//创建布尔型变量帮助判断
}

class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{	
			synchronized(r)//用唯一的资源作锁
			{	
				if(r.flag)
					try
					{
						r.wait();//如果有资源,等待资源取出
					}
					catch (Exception e)
					{
					}
				if(x==0)//操作同一资源
				{
					r.name="Mike";
					r.sex="man";     
				}
				else
				{
					r.name="丽丽";
					r.sex="女女女女女";
				}
				x=(x+1)%2;//创造0,1互换的循环体
				r.flag=true;//表示有资源
				r.notify();//唤醒等待
			}
		}
	}
}

class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while (true)
		{
			synchronized(r)//用唯一的资源作锁
			{	
				if(!r.flag)
					try
					{
						r.wait();如果没有资源,等待存入资源	
					}
					catch (Exception e)
					{
					}
				System.out.println(r.name+"----"+r.sex);//操作同一资源
				r.flag=false;//没有资源
				r.notify();//唤醒等待
			}
		}
	}
}

class InputOutputDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();//创建资源对象

		Input in=new Input(r);//操作同一资源
		Output out=new Output(r);

		Thread t1=new Thread(in);//与线程相关联
		Thread t2=new Thread(out);
		t1.start();
		t2.start();
	}
}

结果输出:

代码优化:

class Res//操作的资源
{
	String name;
	String sex;
	boolean flag=false;//创建布尔型变量帮助判断

	public synchronized void set(String name,String sex)
	{
		if(flag)
			try
			{
				this.wait();//如果有资源,等待资源取出
			}
			catch (Exception e)
			{
			}
		this.name=name;
		this.sex=sex;
		flag=true;//表示有资源
		this.notify();//唤醒等待
	}
	public synchronized void out()
	{		
		if(!flag)
			try
			{
				this.wait();//如果没有资源,等待存入资源	
			}
			catch (Exception e)
			{
			}
		System.out.println(this.name+"----"+this.sex);//操作同一资源
		flag=false;//没有资源
		this.notify();//唤醒等待
	}
}

class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{	
			if(x==0)//操作同一资源
			{
				r.set("Mike","man");
			}
			else
			{
				r.set("丽丽","女女女女女");
			}
			x=(x+1)%2;//创造0,1互换的循环体
		}
	}
}

class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while (true)
		{
			r.out();
		}
	}
}

class InputOutputDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();//创建资源对象

		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();
	}
}
思考1:wait(),notify(),notifyALL(),用来操作线程为什么定义在了Object类中?
a.这些方法存在于同步中。
b.使用这些方法时必须要标识所属的同步的锁。
c.锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

思考2:wait(),sleep()有什么区别?
wait():释放资源,释放锁。
sleep():释放资源,不释放锁。
例3
//消费者和生产者

class Res//操作的资源
{
	private String name;
	private int count=1;
	boolean flag=false;//创建布尔型变量帮助判断

	public synchronized void set(String name)
	{
		while(flag)
			try
			{
				this.wait();//如果有资源,等待资源卖出
			}
			catch (Exception e)
			{
			}
		this.name=name+"---"+count++;//在命名的同时编号
		
		System.out.println(Thread.currentThread().getName()+"........生产者"+this.name);//显示线程名和本类名
		flag=true;//表示有资源
		this.notifyAll();//唤醒等待
	}
	public synchronized void out()
	{		
		while(!flag)
			try
			{
				this.wait();//如果没有资源,等待存入生产	
			}
			catch (Exception e)
			{
			}
		System.out.println(Thread.currentThread().getName()+"消费者"+this.name);//显示线程名和本类名
		flag=false;//没有资源
		this.notifyAll();//唤醒等待
	}
}

class Producer implements Runnable
{
	private Res r;
	Producer(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{	
			r.set("+商品+");
		}
	}
}

class Consumer implements Runnable
{
	private Res r;
	Consumer(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while (true)
		{
			r.out();
		}
	}
}

class ProducerConsumerDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();//创建资源对象

		new Thread(new Producer(r)).start();//多线程生产
		new Thread(new Producer(r)).start();
		new Thread(new Consumer(r)).start();
		new Thread(new Consumer(r)).start();//多线程消费
	}
}
结果输出:
 

如图:

升级解决方案

JDk1.5 中提供了多线程升级解决方案。
将同步synchronized替换成显示Lock操作。
将Object中的wait,notify,notifyAll,替换成了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
例4
import java.util.concurrent.locks.*;

class Res//操作的资源
{
	private String name;
	private int count=1;
	boolean flag=false;//创建布尔型变量帮助判断

	private Lock lock=new ReentrantLock();//创建锁

	private Condition condition_pro=lock.newCondition();//创建锁的对象
	private Condition condition_con=lock.newCondition();//创建锁的对象
	
	public void set(String name)throws InterruptedException
	{
		lock.lock();//安锁
		try
		{
			while(flag)
				condition_pro.await();//生产者等待
			this.name=name+"---"+count++;//在命名的同时编号
			
			System.out.println(Thread.currentThread().getName()+"........生产者"+this.name);//显示线程名和本类名
			flag=true;//表示有资源
			condition_con.signal();//唤醒消费者
		}
		//释放资源
		finally
		{
			lock.unlock();//释放锁的动作一定要执行
		}
	}

	public void out()throws InterruptedException
	{	
		lock.lock();
		try
		{
			while(!flag)
				condition_con.await();
			System.out.println(Thread.currentThread().getName()+"消费者"+this.name);//显示线程名和本类名
			flag=false;//没有资源
			condition_pro.signal();//唤醒生产者
		}
		//释放资源
		finally
		{
			lock.unlock();//释放锁的动作一定要执行
		}
	}
}

class Producer implements Runnable
{
	private Res r;
	Producer(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{	
			try
			{
				r.set("+商品+");
			}
			catch (InterruptedException e)
			{
			}		
		}
	}
}

class Consumer implements Runnable
{
	private Res r;
	Consumer(Res r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while (true)
		{
			try
			{
				r.out();
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}

class ProducerConsumerDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();//创建资源对象

		new Thread(new Producer(r)).start();//多线程生产
		new Thread(new Producer(r)).start();
		new Thread(new Consumer(r)).start();
		new Thread(new Consumer(r)).start();//多线程消费
	}
}


四、停止线程

1、如何停止线程?

stop方法已经过时。

只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。

只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:
当线程处于䠾冻结状态(wait或sleep)
就不会读取到标记,那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供该方法interrupt()
例1
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 changeFlag()//封装转换方法
	{
		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.start();//开启线程
		t2.start();

		int num=0;

		while(true)
		{
			if (num++==60)
			{
				//st.changeFlag();
				t1.interrupt();//清除冻结状态
				t2.interrupt();
				break;//跳出
			}
			System.out.println(Thread.currentThread().getName()+"....."+num);
		}
		System.out.println("OVER");
	}
}
结果输出:

2、守护线程

格式:线程名.setDaemon  将该线程标记为守护线程或用户线程
当正在运行的线程都是守护线程或用户线程时,JAVA虚拟机自动退出。
该方法必须在启动线程前调用。
若设置主函数创建的两条线程为守护线程,则当主函数运行完时,
两线程停止。
在其中主线程为前台线程,创建的两线程为后台线程,前台线程一结束,后台线程也随之关闭。

3、join方法(个人戏称:插队方法)

当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
如:
Thread t1=new Thread(st); //创建线程
Thread t2=new Thread(st);


t1.start();//开启线程
t1.join()://表示0线程拥有优先执行权,1线程和主线程等它运行完后才能执行。
t2.start();

4、setPriority()方法

用来设置优先级
MAX_PRIORITY 优先级为10,最大
NORM_PRIORITY 优先级为5,分配给线程默认优先级
MIN_PRIORITY 优先级为1,最小

5、yield()方法

暂停当前线程,让其他线程执行

6、什么时候写多线程?

当某些代码需要被执行时,就需要用单独的线程进行封装。
例2
class ThreadDemo
{
	public static void main(String[] args)
	{
		new Thread()//运用匿名内部类开启线程
		{	
			public void run()
			{
				for (int x=0;x<60 ;x++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		}.start();

		Runnable r=new Runnable()//另一种开启线程方法
		{
			public void run()
			{
				for (int x=0;x<60 ;x++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		}
		
		for (int x=0;x<60 ;x++ ) //主线程跑起
		{
			System.out.println(Thread.currentThread().getName()+"...."+x);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值