实现多线程的方式主要有两种:继承Thread类;实现Runnable接口
Thread类实现了Runnable接口:
第一种:重写Thread的run方法,将要执行的代码放进去。
Thread thread1 = new Thread(){
public void run(){
*****;
}
}
如果多次调用start()方法,会出现异常Exception in thread “main” java.lang.IllegalThreadStateException。
线程挂起随机时间:
int time = (int) (Math.random()*1000)
Thread.sleep(time)
执行start()方法的顺序并不代表线程启动的顺序。
第二种:实现Runnable接口:
如果想要创建的线程已经有一个父类了,那么就不能在继承Thread了,必须实现Runnable接口。
我们先来看Thread的构造函数:
Thread()
Thread(Runnable target)
Thread(Runnable target,String name)
Thread(String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group,Runnable target, String name)
Thread(ThreadGroup group, Runnable target,String name,long stackSize)
将target作为运行对象,指定name为名称,作为group线程组中的一员,并具有指定的堆栈大小。
Thread(ThreadGroup group,String name
Thread类也实现了Runnable接口,所以也可以用Thread类的对象来传入Thread(Runnable target),这样就可以将一个Thread对象中的run()方法交由其他的线程进行调用。
实例变量与线程安全:
首先,要是能线程共享的数据一定是因为所有线程都调用了同一个对象的同一变量,如果是分别在每个线程对象里的同名变量,则数据不共享。
要实现共享数据,可以使用将同一个拥有实例变量的线程作为runnable参数传入多个Thread构造函数中,这样多个线程便共享一个实例变量。但此时线程不安全,因为很多操作不是原子性的,比如 i--,分为取得原有i值、计算i-1、对i赋值三步,这中间如果有其他线程访问,那么就会出现问题。
如果不希望同步,则可以对方法使用synchronized关键字,如
synchronized public void run(){
super.run();
****
}
非线程安全:多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
i--与System.out.println()的异常
使用System.out.println( i-- )时,可能会出现线程不安全。
实际上print方法内部是同步的,源码如下:
public void println(String x){
synchronized (this){
print(x);
newLine();
}
}
但是i--在进入println之前发生的,所以有可能是线程不安全的,所以还是得使用同步方法。
currentThread():
Thread对象的构造函数运行在主线程中,而run方法运行在新建线程中。
Thread.currentThread().getName()返回正在活动的线程名
this.getName()返回对象的线程名,子类没有重写这个方法就会调用父类的,如Thread-0/Thread-1等等,具体原因可见点击打开链接
isAlive()方法:
判断当前线程是否处于活动状态。即线程已经启动切尚未终止,处在正在运行或者准备开始运行的状态。(开始运行比如已经start但是还没运行)
thread对象.isAlive()
Thread.currentThread().isAlive()
this.isAlive()
sleep()方法:
让currentThread休眠
getId()方法:
取得线程的唯一标识。会得到一个int ID,如直接在主线程中调用,返回 1.
停止线程:
- Thread.stop()方法,最好不用,不安全(despecated
- Thread.interrupt()方法。
3种终止正在运行线程的方法:
- run()方法完成后正常退出
- 使用stop()
- interrupt
interrupt:
不会阻止线程运行,只是会修改是否中断的标志
interrupted():测试当前活动线程是否已经中断状态,执行后会将状态标志改为false。不断被Thread还是Thread对象调用,都是判断当前活动线程。
isInterrupted():测试对象是否是中断状态,且不清除状态标识。
使用异常打断线程运行:
可使用循环中检测interrupted()来中断线程,但是依旧会运行循环外的代码。所以此时利用异常,检测到中断,就抛出异常,直接终止线程中所有代码。
sleep时被interrupt:
会进入异常,同时清除状态值,使之变成false。
如果先被置成中断状态,然后再遇到sleep(),同样也会报异常sleep interrupt
stop()方法:
对象.stop()暴力停止。会抛出java.lang.ThreadDeath异常
return停止线程:
将抛出异常改为在循环检测中return,即可安全退出线程。
不过建议使用“抛异常”的方法来终止线程,因为catch块中可以将异常向上抛,使得线程停止的时间得以传播。
暂停线程:
myThread.suspend()
myThread.resume()
线程暂停和恢复:suspend是过期的方法
缺点:线程暂停时可能会独占某些公共对象,比如sychronized的方法,这样其他线程就无法使用。同时,如果不是同步的,那么也可能出现其他线程导致数据不同步的情况。
例子:println()方法是同步的,如果在线程中暂停在了println方法种,那么其他线程就不能调用println方法