1、创建多线程的三种方式
- 继承Thread
- 实现Runnable接口
1、通过继承Thread类来创建并启动多线程的一般步骤如下:
1)定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2)创建Thread子类的实例,也就是创建了线程对象
3)启动线程,即调用线程的start()方法
代码实例:
public calss MyThread extends Thread{ //继承Thread类
public void run(){
//重写run()方法
//实现业务逻辑
}
}
复制代码
main方法调用:
public calss Main{
public static void main(String[] args){
MyThread mt = new MyThread(); //创建线程对象
mt.start(); //启动线程
}
}
复制代码
2、通过实现Runnable接口创建并启动线程一般步骤如下:
1)定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2)创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3)第三步依然是通过调用线程对象的start()方法来启动线程
代码实例:
public calss MyThread implements Runnable{
public void run(){
//重写run()方法
//实现业务逻辑
}
}
复制代码
public calss Main{
public static void main(String[] args){
MyThread my = new MyThread();
Thread thread = new Thread(my);
thread().start();//或者 new Thread(new MyThread()).start();
}
}
复制代码
2、一些常用方法
1 currentThread()
返回对当前正在执行的线程对象的引用。
2 getId()
返回此线程的标识符
3 getName()
返回此线程的名称
4 getPriority()
返回此线程的优先级
5 isAlive()
测试这个线程是否还处于活动状态。
什么是活动状态呢?
活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。
6 sleep(long millis)
使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
7 interrupt()
中断这个线程。
8 interrupted() 和isInterrupted()
interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能
isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志
9 setName(String name)
将此线程的名称更改为等于参数 name 。
10 isDaemon()
测试这个线程是否是守护线程。
11 setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。
12 join()
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行
13 yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。注意:放弃的时间不确定,可能一会就会重新获得CPU时间片。
14 setPriority(int newPriority)
更改此线程的优先级
3、线程同步问题
-
在需要同步的方法的方法签名中加入synchronized关键字。
public synchronized void accessVal(int newVal);
synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
-
使用synchronized块对需要进行同步的代码段进行同步。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。
synchronized(syncObject){ //允许访问控制的代码 } 复制代码
示例:
public class TestSync {
public static void main(String[] args) {
Account a1 = new Account(100, "高");
Drawing draw1 = new Drawing(80, a1);
Drawing draw2 = new Drawing(80, a1);
draw1.start(); // 你取钱
draw2.start(); // 你老婆取钱
}
}
/*
* 简单表示银行账户
*/
class Account {
int money;
String aname;
public Account(int money, String aname) {
super();
this.money = money;
this.aname = aname;
}
}
/**
* 模拟提款操作
*
* @author Administrator
*
*/
class Drawing extends Thread {
int drawingNum; // 取多少钱
Account account; // 要取钱的账户
int expenseTotal; // 总共取的钱数
public Drawing(int drawingNum, Account account) {
super();
this.drawingNum = drawingNum;
this.account = account;
}
@Override
public void run() {
draw();
}
void draw() {
synchronized (account) {
if (account.money - drawingNum < 0) {
System.out.println(this.getName() + "取款,余额不足!");
return;
}
try {
Thread.sleep(1000); // 判断完后阻塞。其他线程开始运行。
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingNum;
expenseTotal += drawingNum;
}
System.out.println(this.getName() + "--账户余额:" + account.money);
System.out.println(this.getName() + "--总共取了:" + expenseTotal);
}
}
复制代码