多线程即在同一时间,可以做多件事情。
线程的创建和启动
方法一:继承Thread类创建线程类
1.创建一个类,并继承Thread类,重写run方法,该run方法就是线程执行体
2.创建类的实例
3.调用该实例的start()方法
注意:
用这种方法创建的线程类的全局变量是不会共享的;
类的Main方法是一个主线程;
用户启动的多个线程的名字默认依次为Thread-1,Thread-2…Thread-n
public class scanner extends Thread
{
public void run()
{
//线程执行体
------获取当前线程可以直接用this.getName(),this可以省去
}
public static void main(String[] args) {
new scanner().start();------创建第一个线程
new scanner().start();-------创建第二个线程
}
}
方法二:实现Runnable接口创建线程类
1.定义Runnable接口的实现类,并重写该接口的run方法
2.创建Runnable实现类的实例,并一此实例作为Thread的target来创建线程对象
3.调用线程对象的start()方法来启动该线程
注意:用该方法创建的线程,可以共享线程类的全局变量
public class scanner implements Runnable
{
public void run()
{
//线程执行体
------获取当前线程用Thread.currentThread().getName();
------和方法一比较会复杂些
}
public static void main(String[] args) {
scanner scanner = new scanner();
new Thread(scanner,"线程名字").start();//创建线程一
new Thread(scanner,"线程名字").start();//创建线程二
}
}
方法三:匿名类
//匿名类
Thread t1= new Thread(){
public void run(){
----匿名类中用到外部的局部变量teemo,必须把teemo声明为final
-----但是在JDK7以后,就不是必须加final的了
while(!teemo.isDead()){
gareen.attackHero(teemo);
}
}
};
t1.start();
常用方法
- sleep(毫秒) 当前线程暂停
- join() 加入到当前线程中
- setPriority() 线程优先级
- yield() 临时暂停
- setDaemon() 守护线程
- currentThread() 获取当前线程
- getName(): 获取当前线程的名字
- setName(): 设置当前线程的名字
join线程
当某个程序执行流中调用其它线程的join()方法时,调用线程将被阻塞,直到join线程执行完
在for循环外面时是主线程和Thread-1在并发执行,到了for循环中,Thread-2使用了join方法,也就导致主线程必须等待Thread-2执行结束才会继续执行
- join():等待被join的线程执行完成
- join(long millis):等待被join的线程的时间最长为millis毫秒,如果在millis毫秒内被join的线程还没有执行结束,则不在等待
public static void main(String[] args) throws InterruptedException {
new Thread().start();------创建第一个线程
for(int i =0;i<100;i++)
{
if(i==20)
{
scanner scanner = new scanner();
scanner.start();-------创建第二个线程
scanner.join();------join线程
}
}
}
public class TestThread {
public static void main(String[] args) {
final Hero gareen = new Hero();
gareen.name = "盖伦";
gareen.hp = 616;
gareen.damage = 50;
final Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 300;
teemo.damage = 30;
final Hero bh = new Hero();
bh.name = "赏金猎人";
bh.hp = 500;
bh.damage = 65;
final Hero leesin = new Hero();
leesin.name = "盲僧";
leesin.hp = 455;
leesin.damage = 80;
Thread t1= new Thread(){
public void run(){
while(!teemo.isDead()){
gareen.attackHero(teemo);
}
}
};
t1.start();
---代码执行到这里,一直是main线程在运行
try {
---t1线程加入到main线程中来,只有t1线程运行结束,才会继续往下走
t1.join();
} catch (InterruptedException e) {
---- TODO Auto-generated catch block
e.printStackTrace();
}
Thread t2= new Thread(){
public void run(){
while(!leesin.isDead()){
bh.attackHero(leesin);
}
}
};
-----会观察到盖伦把提莫杀掉后,才运行t2线程
t2.start();
}
}
后台线程
后台线程:是指在后台运行,为其它线程提供给服务的线程,又称为“守护线程”。
后台线程的特征:如果所有的前台线程都死亡,后台线程会自动死亡
- setDaemon(true):将指定线程设置成后台线程
setDaemon(true)必须在start()之前使用
线程睡眠
*static sleep(long millis):将使当前正在执行的线程暂停millis毫秒,将使线程进入阻塞状态
线程让步:yieId
- yieId():也是Thread的一个静态方法,他会让当前正在执行的线程暂停一下,但它不会阻塞该线程,而是将该线程转入就绪状态
yieId()与sleep()的区别:
1.sleep暂停后会其它线程执行机会,不会理会其它线程的优先级,但是yieId只会给优先级相同或更高的线程执行机会
2.sleep方法比yieId方法更具有好的可移植性,通常不建议使用yieId()方法来控制并发的执行
改变线程的优先级
每个线程执行时都有一个优先级,优先级越高被执行的概率就越大,而优先级越低的线程则获得较少的执行机会
每个线程默认的优先级都与创建它的父线程的优先级相同,默认情况下maiin线程具有普通优先级,由main线程创建的子线程也具有普通优先级
- setPriority(int newPriority):设置优先级
- getPriority():获得指定线程的优先级
setPriority方法的参数可以是1-10的整数,也可以是Thread类的三个常量
- MAX_PRIORITY:值为10
- MIN_PRIORITY :值是1
- NORM_PRIORITY:值是5
通常建议使用三个常量来表示优先级,因为有些操作系统不支持输入数字来 表示优先级
线程同步
多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题
同步代码块
在方法体内加synchronized(obj){},给对多个线程同时修改一个数据的这个变量相关代码加锁,加锁后当有一个线程在访问这个数据时,其他线程必须等当前线程执行完毕
synchronized(obj){}为同步监视器,里面的object对象是多个线程同时修改一个数据的这个变量的类对象
public void run()
{
synchronized (scanner)
{
//线程执行体
Thread.currentThread().getName();
}
}
同步方法
再方法的返回值前加synchronized ,给代码加锁是需要消耗运行效率的,所以我们只对那些会改变共享变量的方法进行同步;如果可变类有两种运行环境:单线程和多线程,则需要为可变量提供两种版本,一种不加锁,一种加锁的
public synchronized void add()
{
//线程执行体
Thread.currentThread().getName();
}