线程生命周期
线程具有生命周期,当线程被创建并启动后,不会立即进入执行状态,也不会一直处于执行状态。
在线程的生命周期中,要经过5种状态:
• 新建
• 就绪
• 运行
• 阻塞
• 死亡
注意点:
• 只能对新建状态的线程调用start方法,且只能调用一次
• 如果调用start()方法后需要线程立即开始执行,可以使用Thread.sleep(1)来让当前运行的主线程休眠1毫秒,此时子线程处于就绪状态的子线程
• 被阻塞的线程阻塞解除后进入就绪状态
相关的一些方法:
- sleep():暂停执行的方法,参数以毫秒为单位,注意抛出异常
- 如果一个线程包含了很长的循环,在循环的每次迭代之后把该线程切换到sleep休眠状态时一种很好的策略,这可以保证其他线程不必等待很长时间就能轮到处理器执行。
- isALive():判断线程是否处于执行状态,当处于就绪,运行,阻塞状态时返回true
- stop():直接停止线程,容易导致死锁,通常不推荐使用
- join():可以让一个线程等待另一个线程完成后,继续执行原线程中的任务。当在某个程序执行流中调用其他线程的join方法时,当前线程将会被阻塞,直到另一个线程执行完成为止。
关于join的使用的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package 线程;
class JoinThread extends Thread { public JoinThread(String name) { super(name); }
@Override public void run() { for (int i = 0; i <= 10; i++) System.out.println(Thread.currentThread().getName() + ":" + i); } }
public class JoinDemo { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); JoinThread jThread = new JoinThread("子线程"); jThread.start(); System.out.println(Thread.currentThread().getName()); try { jThread.join(); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); System.out.println(jThread.isAlive()); } }
|
结果如下:
线程优先级
- 每个线程执行时都有一定的优先级,系统根据优先级调度线程
- 每个线程都有默认的优先级,与父线程相同
- 主线程具有普通优先级
- Thread类提供三个静态常量来标识线程的优先级
- MAX_PRIORITY 最高优先级,其值为10
- NORM_PRIORITY 普通优先级,其值为5,默认优先级
- MIN_PRIORITY 最低优先级,值为1
- 优先级高的线程提前获得执行的机会也会更多
线程同步
多线程访问同一资源会带来安全问题,java中提供了线程同步的概念来保证某个资源在
某一时刻只能由一个线程访问,保证共享数据的一致性
线程同步通常采用以下三种方式:
同步代码块:
将对实例的访问语句放入一个同步块中,语法格式如下:
1 2 3
| synchronized(object){
}
|
其中synchronized是关键字,object是监视器
实例,客户在银行中存钱,账户唯一,用线程进行存取钱操作,初始余额5000.
假设客户进行了5次操作,如果没有同步,可能执行时候每个操作都以初始值5000为起点,最后得到的值是错的;
而执行同步操作后,每次只进行一种操作,以上次操作结果为起点,可以保证操作的正确性
同步方法:
使用synchronized关键字修饰需要同步的方法,语法格式如下:
1 2 3
| [访问修饰符]synchronized 返回类型 方法名(参数){
}
|
一个具有同步方法的类也被称为“线程安全类”
synchronized锁定的是对象,而不是代码块或方法,也可以修饰类
同步锁:
Lock是控制多个线程对共享资源进行访问的工具,能够对共享资源进行独占访问
有ReentrantLock(可重入锁)实现类
步骤:
- 定义一个ReentrantLock锁对象,该对象是final常量
- 在需要保证线程安全的代码之前增加“加锁”操作
- 在执行完线程安全的代码后释放锁
代码示例(使用同步方法,同步锁):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| package 线程;
import java.util.concurrent.locks.ReentrantLock;
public class BankAccount { private String bankNo; private double balance; private final ReentrantLock lock=new ReentrantLock(); public BankAccount(String bankNO, double balance) { this.bankNo = bankNO; this.balance = balance; }
public double getBalance() { return balance; }
public String getBankNo() { return bankNo; }
public void setBalance(double balance) { this.balance = balance; }
public void setBankNo(String bankNo) { this.bankNo = bankNo; } public synchronized void access(double money) { try { if(money<0&balance<-money) { System.out.println("余额不足,操作失败"); return; } else { balance+=money; System.out.println(Thread.currentThread().getName()+"操作成功,目前余额为:"+balance); try { Thread.sleep(1); }catch (InterruptedException e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); }finally { } } }
package 线程;
public class BankLock extends Thread { private BankAccount account; private double money;
public BankLock(String name, BankAccount account, double money) { super(name); this.money = money; this.account = account; }
@Override public void run() { this.account.access(money); }
public static void main(String[] args) { BankAccount cAccount = new BankAccount("WT", 5000); BankLock t1 = new BankLock("task1", cAccount, -3000); BankLock t2 = new BankLock("task2", cAccount, -3000); BankLock t3 = new BankLock("task3", cAccount, 1000); BankLock t4 = new BankLock("task4", cAccount, -2000); BankLock t5 = new BankLock("task5", cAccount, 2000); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); try { t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); } catch (Exception e) { e.printStackTrace(); } System.out.println("账户:" + cAccount.getBankNo()); System.out.println("余额:" + cAccount.getBalance()); } }
|
线程通信
加入目前系统中有生产和消费两个线程,系统要求不断重复生产,消费操作,并要求每当一个线程生产后,另一个线程立即进行消费,不允许连续两次生产,也不允许连续两次消费,实现这种功能,可以采用线程间的通信技术
相关方法: