一、多线程概述
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方法。
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象时Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
因为,自定义的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时,能解决安全问题。
函数需要被对象调用,那么函数都有一个所属对象引用,就是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类中。
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():释放资源,不释放锁。
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锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
将同步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()
只有一种,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();
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);
}
}
}