线程的基本使用
创建线程的两种方式
1.继承Thread类,重写run方法
public class Date {
public static void main ( String [ ] args) {
Aa aa = new Aa ( ) ;
aa. start ( ) ;
}
}
class Aa extends Thread {
@Override
public void run ( ) {
while ( true ) {
System . out. println ( "123" ) ;
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
}
}
}
打印线程名称
Thread . currentThread ( ) . getName ( )
为什么程序中调用的不是run方法,而是start方法?
答:如果直接调用run方法,其实是main主线程直接调用run方法,打印线程名显示的是main线程,并没有开启另一个线程
源码解读:
追进start方法:public synchronized void start ( )
里面有个 start0 是本地方法,由JVM调用,底层是C / C ++ 实现
真正实现多线程的是start0方法:
private native void start0 ( ) ;
2.实现Runable接口
public class Date {
public static void main ( String [ ] args) {
Dog dog = new Dog ( ) ;
Thread thread = new Thread ( dog) ;
thread. start ( ) ; 采用了代理模式
或
new Thread ( new Dog ( ) ) . start ( ) ;
}
}
class Dog implements Runnable {
@Override
public void run ( ) {
while ( true ) {
System . out. println ( "123" ) ;
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
}
}
}
3.继承Thread VS 实现Runnable的区别
1 、从Java 的设计来看,通过继承Thread 或者实现Runnable 接口来创建线程本质上没有,Thread 类本身就实现了Runnable 接口
2 、实现Runnable 接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
线程终止
1 、当线程完成任务后,自动退出
2 、还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
D d = new D ( ) ;
d. start ( ) ;
System . out. println ( "主线程休眠10s" ) ;
Thread . sleep ( 10000 ) ;
d. setLoop ( false ) ;
}
}
class D extends Thread {
private boolean loop = true ;
@Override
public void run ( ) {
while ( loop) {
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
System . out. println ( "线程还在运行" ) ;
}
}
public void setLoop ( boolean loop) {
this . loop = loop;
}
}
线程常用方法
1 、setName 设置线程名称,使之与参数name相同
2 、getName 返回该线程的名称
3 、start 使线程开始执行;Java 虚拟机底层调用该线程的start0方法
start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动柜新线程
4 、run 调用线程对象run方法
5 、setPriority 更改线程的优先级
线程优先级的范围(常用):MAX_PRIORITY 10
MIN_PRIORITY 1
NORM_PRIORITY 5
6 、getPriority 获取线程的优先级
7 、sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂时执行)
8 、interrupt 中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠的线程
9 、yield 线程的礼让。让出CPU,让其他线程执行,但执行的时间不确定,所以也不一定礼让成功
如果资源足够,不一定礼让成功
10 、join 线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有任务
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
D d = new D ( ) ;
d. setName ( "fsp" ) ;
d. setPriority ( Thread . MIN_PRIORITY) ;
d. start ( ) ;
for ( int i = 0 ; i < 5 ; i++ ) {
Thread . sleep ( 1000 ) ;
System . out. println ( "hi" + i) ;
}
d. interrupt ( ) ;
}
}
class D extends Thread {
@Override
public void run ( ) {
while ( true ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + " " + Thread . currentThread ( ) . getPriority ( ) + " " + i) ;
}
try {
System . out. println ( "休眠中...." ) ;
Thread . sleep ( 10 * 1000 ) ;
} catch ( InterruptedException e) {
}
}
}
}
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
D d = new D ( ) ;
d. start ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
Thread . sleep ( 3000 ) ;
System . out. println ( "main" + ( i+ 1 ) ) ;
if ( i == 5 ) {
d. join ( ) ;
}
}
}
}
class D extends Thread {
int n = 0 ;
@Override
public void run ( ) {
while ( true ) {
System . out. println ( "D线程" + ( ++ n) ) ;
try {
Thread . sleep ( 3000 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
if ( n == 20 ) {
break ;
}
}
}
}
用户线程和守护线程
1 、用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2 、守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3 、常见的守护线程:垃圾回收机制
main线程执行10 次后退出,run方法设置成守护线程,也退出。
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
D d = new D ( ) ;
d. setDaemon ( true ) ; 设置成守护线程后再调用start()方法
d. start ( ) ;
for ( int i = 1 ; i <= 10 ; i++ ) {
System . out. println ( "bbbbbbbbb" + i) ;
Thread . sleep ( 1000 ) ;
}
}
}
class D extends Thread {
int n = 1 ;
@Override
public void run ( ) {
while ( true ) {
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
System . out. println ( "aaaaaa" + ( n++ ) ) ;
}
}
}
线程的生命周期
线程七大状态
在JDK中用Thread. State 枚举表示了线程的几种状态
public static enum Thread. State
extends Enum < Thread. State >
线程状态。线程可以处于以下状态之一:
NEW: 尚未启动的线程处于此状态
RUNNABLE: 在Java 虚拟机中执行的线程处于此状态
BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
WAITING: 正在等待另一个线程执行特定动作的线程处于此状态
TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED: 已退出的线程处于此状态
线程状态转换图
当new 了一个线程后,它首先进入新创建的状态叫NEW;
一旦调用看start()方法,就进入了可运行状态Runnable 。
Runnable 细化为两个状态:Ready (就绪)和Running (运行)
Ready 要不要执行,还要取决于线程是否被调度器选中,如果选中了就会进入到Running 状态。
如果线程处于运行状态时,时间到了,这个时间直接进入到Teminated (终止状态)。
如果在Runnable 状态中,执行了等待进入同步代码块的锁,会进入Blocked 阻塞状态,如果重新获得锁了会再次进入Runnable 状态。
Runnable 状态只是表示可以运行,但是不一定直接Running ,什么时候运行由操作系统来控制。
如果线程在Runnable 状态调用了wait或join,它会进入Wait 状态,可以通过notify或者notifyAll重新进入Runnable 状态。
在Runnable 状态中突然调用sleep(time),会进入到超时等待状态,时间结束后再次进入到Runnable 状态
线程的同步
线程同步机制
1 、在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
2 、也可以这样理解L 线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,
直到该线程完成操作,其他线程才能对该内存地址进行操作
同步具体方法
1 、同步代码块
synchronized ( 对象) { 得到对象的锁,才能操作同步代码
需要被同步代码
}
2 、synchronized 还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m ( String name) {
需要被同步的代码
}
模拟三个窗口的售票
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
Sell sell = new Sell ( ) ;
new Thread ( sell) . start ( ) ;
new Thread ( sell) . start ( ) ;
new Thread ( sell) . start ( ) ;
}
}
class Sell implements Runnable {
private int t = 100 ;
private boolean loop = true ;
public synchronized void sell02 ( ) { 同步方法,在同一时刻只能有一个线程来执行run方法
if ( t <= 0 ) {
System . out. println ( "售票结束....." ) ;
loop = false ;
return ;
}
try {
Thread . sleep ( 50 ) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "窗口售出一张票," + "剩余票数:" + ( -- t) ) ;
}
@Override
public void run ( ) {
while ( loop) {
sell02 ( ) ;
}
}
}
互斥锁
1 、java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
2 、每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
3 、关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问
4 、同步的局限性:导致程序的执行效率要降低
5 、同步方法(非静态的)的锁可以是this ,也可以是其他对象(要求是同一个对象)
6 、同步方法(静态)的锁为当前类本身
注意事项和细节
1 、同步方法如果没有使用static 修饰:默认锁对象为this
2 、如果方法使用static 修饰,默认锁对象:当前类. class
3 、实现的落地步骤:
* 需要先分析上锁的代码
* 选择同步代码块或同步方法
* 要求多个线程的锁对象为同一个即可
线程的死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的方法
释放锁
下面操作会释放锁:
1 、当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
2 、当前线程在同步代码块。同步方法中遇到break 、return
案例:没有正常的完事,经理叫他修改bug,不得已出来
3 、当前线程在同步代码块、同步方法中出现了未处理的Error 或Exception ,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来
4 、当前线程在同步代码块、同步方法中执行了线程对象的wait ( ) 方法,当前线程暂停,并释放锁
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去
下面操作不会释放锁:
1 、线程执行同步代码块或同步方法时,程序调用Thread . sleep ( ) 、Thread . yield ( ) 方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,眯了一会
2 、线程执行同步代码块时,其他线程调用了该线程的suspend ( ) 方法将该线程挂起,该线程不会释放锁
提示:应尽量避免使用suspend ( ) 和resume ( ) 来控制线程,方法不再推荐使用
练习
(1 )在main方法中启动两个线程
(2 )第1 个线程循环随机打印100 以内的整数
(3 )直到第2 个线程从键盘读取了“Q ”命令
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
Aa aa = new Aa ( ) ;
Bb bb = new Bb ( aa) ;
aa. start ( ) ;
bb. start ( ) ;
}
}
class Aa extends Thread {
public boolean loop = true ;
@Override
public void run ( ) {
while ( loop) {
System . out. println ( ( int ) ( Math . random ( ) * 100 + 1 ) ) ;
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
public void setLoop ( boolean loop) {
this . loop = loop;
}
}
class Bb extends Thread {
Scanner scanner = new Scanner ( System . in) ;
private Aa aa;
public Bb ( Aa aa) {
this . aa = aa;
}
@Override
public void run ( ) {
while ( true ) {
System . out. println ( "输入一个字符:" ) ;
char n = scanner. next ( ) . toUpperCase ( ) . charAt ( 0 ) ;
System . out. println ( ) ;
if ( n == 'Q' ) {
aa. setLoop ( false ) ;
System . out. println ( "线程退出" ) ;
break ;
}
}
}
}
(1 )有2 个用户分别从同一个卡上取钱(总额:10000 )
(2 )每次都取1000 ,当余额不足时,就不能再取款了
(3 )不能出现超取现象== = 》线程同步问题
public class Date {
public static void main ( String [ ] args) throws InterruptedException {
Aa aa = new Aa ( ) ;
Thread thread = new Thread ( aa) ;
thread. setName ( "t1" ) ;
Thread thread1 = new Thread ( aa) ;
thread1. setName ( "t2" ) ;
thread. start ( ) ;
thread1. start ( ) ;
}
}
class Aa implements Runnable {
private int money = 10000 ;
@Override
public void run ( ) {
while ( true ) {
synchronized ( this ) {
if ( money < 1000 ) {
System . out. println ( "余额不足" ) ;
break ;
}
money -= 1000 ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + " 取出了1000 当前余额=" + money) ;
}
try {
Thread . sleep ( 1000 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
}