线程
一、 继承Thread类创建线程类
步骤:
① 定义Thread类的子类,重写该类的run()方法,该方法就是线程执行体;
② 创建Thread子类的实例,即创建子线程对象;
③ 调用子线程的start()方法启动该线程。
public class FirstThread extends Thread
{
private int i ;
public void run()
{
for( ; i < 100 ; i++)
{
System.out.println(getName()+ " " + i);
}
}
public static voidmain(String args[])
{
for(int i = 0; i <100; i ++)
{
System.out.println(Thread.currentThread().getName()+ " " + i);
if(i == 20)
{
newFirstThread().start();
newFirstThread().start();
}
}
}
}
进行多线程的编程时不要忘记java程序运行时默认的主线程,main()方法的方法体就是主线程的执行体。
使用继承Thread类的方法创建线程时,多个线程之间无法共享线程类的实例变量。
二、 实现Runnable接口创建线程类
步骤:
① 定义Runnable接口的实现类,并重写该接口的run()方法,该方法的方法体就是线程的执行体;
② 创建Runnable实现类的实例,并将此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象;
③ 用真正的Thread对象调用start()方法启动线程。
public class SecondThread implements Runnable
{
private int i ;
public void run()
{
for( ; i < 100; i++){
System.out.println(Thread.currentThread().getName()+ " " + i);
}
}
public static voidmain(String args[])
{
for(int i = 0; i <100; i ++)
{
System.out.println(Thread.currentThread().getName()+ " " + i);
if (i == 20)
{
SecondThreadst = new SecondThread();
new Thread(st, "newThread-1").start();
new Thread(st, "newThread-2").start();
}
}
}
}
上面的线程输出结果中,两个子线程的i变量是连续的,也就是说采用实现Runnable接口创建的多个子线程可以共享线程类的实例变量。这是因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,所以多个线程可以共享同一个线程类的实例变量。
三、 实现Callable接口实现线程类(与Runnable接口类似,只是稍微复杂)
四、 线程控制
Join线程
Thread提供了让一个线程等待另一个线程完成的方法——join()方法。下面的程序中,主线程main要等待“被join的线程”执行完成后才进入运行状态,调用join()方法后main进入阻塞状态。
publicclass JoinThread extends Thread
{
public void run()
{
for (int i = 0; i < 50 ; i ++)
{
System.out.println(getName() +" " + i);
}
}
public static void main(String args[])throws InterruptedException //需要对join进行异常捕获或者抛出异常
{
JoinThread j = new JoinThread();
j.setName("new Thread");
j.start();
for (int i = 0; i < 100 ; i ++ )
{
System.out.println(Thread.currentThread().getName()+ " " + i);
if (i == 20)
{
JoinThread join = newJoinThread();
join.setName("joinThread");
join.start();
join.join(); //main线程调用了join线程的join方法,main线程必须等待join线程执行结束后才会向下执行
}
}
}
}
后台线程
有一种线程是在后台运行的,他的任务是为其他线程服务,被称为后台线程。JVM的垃圾回收线程就是典型的后台线程。
后台线程有个特征,如果所有的前台线程都死亡,后台线程会自动死亡。
调用thread对象的setDaemon(true)可以将指定线程设置成后台线程。
publicclass DaemonThread extends Thread
{
public void run()
{
for (int i = 0; i < 1000 ; i++ )
{
System.out.println(getName() +" " + i);
}
}
public static void main(String args[])
{
DaemonThread d = new DaemonThread();
d.setDaemon(true);//注意这儿的顺序
d.start();
for (int i = 0; i < 50 ; i ++ )
{
System.out.println(Thread.currentThread().getName()+ " " + i);
}
//程序运行到此处,前台线程(main线程)结束,后台线程也随之结束,所以不会输出999
}
}
Thread类还提供了一个isDaemon()方法,用于判断指定线程是否为后台线程。
前台线程死亡后,JVM会通知后台线程死亡,但它从接受指令到做出响应,需要一定时间。如果要将某个线程设置为后台线程,必须在线程启动之前设置(setDaemon()在start()之前),否则会引发IllegalThreadStateException异常。
线程睡眠
线程睡眠调用Thread.sleep(longmillis)方法,之后线程进入阻塞状态。睡眠时间未到时,及时系统中没有其他科执行的线程时,它也不会被唤醒。
线程让步
线程让步调用Thread.yield()方法,之后线程进入就绪状态。等待线程调度器抽象调度。当线程调用yield暂定之后,只有优先级高于或者等于当前线程的就绪状态线程才会获得执行的机会。
五、 线程同步
同步代码块:
publicclass Account
{
private String accountNo ;
private double balance;
public Account(){}
public Account(String accountNo, doublebalance)
{
this.accountNo = accountNo;
this.balance = balance;
}
//定义重写hashCode和equals的方法
public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null &&obj.getClass() == Account.class)
{
Account target = (Account)obj;
returntarget.getAccountNo().equals(accountNo);
}
return false;
}
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}
public void setBalance(double balance)
{
this.balance = balance;
}
public double getBalance()
{
return this.balance;
}
}
publicclass DrawThread extends Thread
{
private Account account;
private double drawAmont;
public DrawThread(String name, Accountaccount, double drawAmont)
{
super(name);
this.account = account;
this.drawAmont = drawAmont;
}
public void run()
{
//使用account作为同步监视器,任何线程进入下面的代码块之前,必须先获得对account账户的锁定
synchronized(account)
{
if (account.getBalance() >=drawAmont)
{
System.out.println(getName() +"取钱成功!吐出钞票:" +drawAmont);
try
{
Thread.sleep(1);
}catch(InterruptedException ex)
{
ex.printStackTrace();
}
account.setBalance(account.getBalance()- drawAmont);
System.out.println("余额为:" + account.getBalance());
}
else
{
System.out.println(getName() +"取钱失败!余额不足");
}
}
}
}
同步方法:
publicclass Account
{
private String accountNo ;
private double balance;
public Account(){}
public Account(String accountNo, doublebalance)
{
this.accountNo = accountNo;
this.balance = balance;
}
public synchronized void draw(doubledrawAmont)
{
if (balance >= drawAmont)
{
System.out.println(Thread.currentThread().getName()+ "取钱成功!吐出钞票:"+ drawAmont);
try
{
Thread.sleep(1);
}catch(InterruptedException ex)
{
ex.printStackTrace();
}
balance -= drawAmont;
System.out.println("余额为:" + balance);
}
else
{
System.out.println(Thread.currentThread().getName()+ "取钱失败!余额不足");
}
}
//定义重写hashCode和equals的方法
public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj != null &&obj.getClass() == Account.class)
{
Account target = (Account)obj;
returntarget.getAccountNo().equals(accountNo);
}
return false;
}
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}
public void setBalance(double balance)
{
this.balance = balance;
}
public double getBalance()
{
return this.balance;
}
}
publicclass DrawThread extends Thread
{
private Account account;
private double drawAmont;
public DrawThread(String name, Accountaccount, double drawAmont)
{
super(name);
this.account = account;
this.drawAmont = drawAmont;
}
public void run()
{
account.draw(drawAmont);
}
}
publicclass DrawTest
{
public static void main(String args[])
{
Account act = newAccount("88888888", 1000);
new DrawThread("甲", act, 800).start();
new DrawThread("乙", act, 800).start();
}
}
同步锁:
Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步,同步锁由Lock对象充当。
比较常用的是ReenTrantLock(可重入锁)。代码格式如下:
classX
{
//定义对象锁
private final ReentrantLock lock = newReentrantLock();
//...
//定义需要保证线程安全的方法
public void m ()
{
//加锁
lock.lock();
try
{
//需要保证线程安全的代码
//...method body
}
catch ()
{
}
//使用finally来保证释放锁
finally
{
lock.unlock();
}
}
}
六、 线程通信
① 对于使用synchronized的同步机制时(同步代码块或者同步方法),由以下三个方法进行线程间的通信:wait(),notify(),notifyAll().这三个方法不属于Thread类,而是属于Object类,这三个方法必须由同步监视器对象来调用。
② 对于使用Lock的同步机制来保证同步时,由以下三个方法进行线程通信:await(),signal(),signalAll().这三个方法由Condition类提供。使用Condition对象来协调线程的运行。
七、 线程池