关于java中的线程主要介绍一下以下几个方面:1、线程的创建和启动 2、线程的生命周期 3、线程同步(重点)
---------------------------------------------------------------------------------
1、线程的创建和启动:
i)线程的创建:线程的创建有两种方式:继承java.lang.Thread类;实现java.lang.Runable接口。
对于继承java.lang.Thread类的线程,在创建了这个线程以后,我们要重写该类的run()方法,而run方法的方法体是当前该线程要做的事情。
对于实现java.lang.Runable接口的线程,在创建了该线程之后,也要实现run()方法。(注意:其中java.lang.Thread这个类也是实现了java.lang.Runable接口的)。eg:
public class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
public class MyThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
以上两个线程分别使用两种方式被创建。run方法都是循环100次输出当前线程的名字。在第二种方式中,由于java.lang.Thread这个类也是实现了java.lang.Runable接口,所以可以使用Thread类的方法currentThread()方法来获得当前线程。
ii)线程的启动:线程的启动要使用start()方法。我们首先创建一个main方法,在main方法中启动线程,eg
public class Test{
public static void main(String[] args){
MyThread1 m1 = new MyThread1();
m1.start();
Thread t2 = new Thread(new MyThread2());
t2.start();
}
}
对于线程二的启动,因为实现的是Runable接口,所以不能直接调用start方法,在Thread类的构造方法中,有一个参数是Runable接口的方法,由于MyThread2实现了该接口,所以使用 Thread类的该构造方法,启动start方法。
建和启动线程,倒不是因为哪一种方式更好,而是考虑到java的单继承,当一个类已经继承了另外一个类时,就不能再去继承Thread类,只能去实现Runable接口。
2、线程的生命周期:
线程只能创建和死亡一次,但是线程的其他三种方式却是可以循环多次的。一个线程被创建后,就会进入就绪队列等待调度,当获得处理器资源后就会进入运行状态,当该线程的run方法全部运行完后,该线程就会死亡,或者就会进入阻塞状态,阻塞结束后重新进入就绪状态。或在运行过程中市区处理器资源,直接进入就绪状态等待,依次循环,直到线程死亡。
3、线程同步:
在多线程中,当多个线程同时对同一个对象的同一个实例变量操作时,就可能引起线程不同步问题。
在此以两个人同时用一个账户取钱为例,说明并解决线程不同步的问题。
public class Account {
private int money = 2000;
public void getMoney(String name,int money) {
//...此处省略1000行
if(money < this.money) {
this.money -= money;
System.out.println(name + "取款成功");
} else {
System.out.println(name + "余额不足");
}
}
}
public class Rose extends Thread{
private Account account;
public Rose(Account account) {
this.account = account;
}
@Override
public void run() {
try {
Thread.sleep(300);
account.getMoney("rose", 1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Tom extends Thread{
private Account account;
public Tom(Account account) {
this.account = account;
}
@Override
public void run() {
try {
Thread.sleep(300);
account.getMoney("tom", 1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args){
Account account = new Account();
Tom tom = new Tom(account);
Rose rose = new Rose(account);
tom.start();
rose.start();
}
}
运行结果截图:
之所以出现不同的结果,是因为对于两个不同的线程Rose和Tom调用account的takeMoney()方法时出现的线程的切换,当第一个线程完成if条件判断时,处理器资源突然给了另一个线程。这就造成了线程不同步,即现成不安全。
解决方案1:在方法声明上添加关键字synchronized,即对于Account类中的方法takeMoney()这样声明:public synchronized void takeMoney(),这样一旦一个线程run方法运行到这个方法时,就会上锁,阻止其他线程进入。从而实现线程同步。
解决方案2:使用synchronized同步代码块。我们只对可能引起线程不同步的那一部分代码上锁,而不是对整个方法上锁,从而提高程序的运行效率。即:将if选择包括在同步代码块中。
public class Account {
private int money = 2000;
public void getMoney(String name,int money) {
//...此处省略1000行
synchronized(this){
if(money < this.money) {
this.money -= money;
System.out.println(name + "取款成功");
} else {
System.out.println(name + "余额不足");
}
} <span></span> }
}
解决方案3:同步锁,引入java.util.concurrent.locks.ReentrantLock。在if选择运行前加锁,运行完这些代码后,在解锁。代码如下:
public class Account {
private int money = 2000;
private ReentrantLock reentrantLock = new ReentrantLock();
public void getMoney(String name,int money) {
//...此处省略1000行
try{
reentrantLock.lock();
if(money < this.money) {
this.money -= money;
System.out.println(name + "取款成功");
} else {
System.out.println(name + "余额不足");
}
} finally {
reentrantLock.unlock();
}
}
}
到此为止,javaSE就算告一段落了。期待后续补充、、、