今天是学习java的第二十九天,今天学习了多线程
线程的状态
线程调度
线程调度指按照特定机制为多个线程分配CPU的使用权
void setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程
boolean isAlive() 测试线程是否处于活动状态
线程优先级
线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大
线程休眠
让线程暂时睡眠指定时长,线程进入阻塞状态
睡眠时间过后线程会再进入可运行状态
public static void sleep(long millis)
millis为休眠时长,以毫秒为单位
调用sleep()方法需处理InterruptedException异常
线程强制运行
使当前线程暂停执行,等待其他线程结束后再继续执行本线程
millis:以毫秒为单位的等待时长
nanos:要等待的附加纳秒时长
需处理InterruptedException异常
线程的礼让
暂停当前线程,允许其他具有相同优先级的线程获得运行机会
该线程处于就绪状态,不转为阻塞状态 public static void yield()
只是提供一种可能,但是不能保证一定会实现礼让
多线程数据共享
public class Site implements Runnable {
// 定义属性表示票库里的票的数量
private int count = 10;
// 定义属性表示用户买到的是第几张票
private int num = 0;
@Override
public void run() {
// 如果票的属性小于0,就不再买票
while (true) {
if (count <= 0) {
break;
}
// 每卖一张票,票的总数要建议,卖的是第几张票,变量要+1
count--;
num++;
// 买票过程中模拟网速缓慢
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买到了第" + num
+ "张票,还剩" + count + "张票");
}
}
}
Site site1 = new Site();
Thread t1 = new Thread(site1,"张三");
Thread t2 = new Thread(site1,"李四");
Thread t3 = new Thread(site1,"王五");
t1.start();
t2.start();
t3.start();
当多个线程操作共享数据的时候容易出现问题
在一个线程操作这个共享数据的时候,还没有完全操作完毕,线程失去了CPU资源,cpu被另一个线程获取,另外一个线程会接着上一个线程操作的数据继续进行操作,当另外一个线程操作数据完毕之后,第一个线程有获取了CPU资源,此时,第一个线程看到的数据是第二个线程操作后的数据,由此就会引发数据不安全的问题
同步方法
使用synchronized修饰的方法控制对类成员变量的访问
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
synchronized就是为当前的线程声明一把锁
使用同步方法的网络购票
public synchronized boolean sale() {
// 如果票的数量小于0,就不在卖票
if (count <= 0) {
return false;
}
// 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
count--;
num++;
// 买票过程中模拟网速缓慢
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买到了第" + num
+ "张票,还剩余" + count + "张票");
return true;
}
// 定义属性表示票库里的票的数量
private int count = 10;
// 定义属性表示用户买到的是第几张票
private int num = 0;
@Override
public void run() {
while (true) {
if(!sale()){
break;
}
}
}
使用synchronized关键字修饰的代码块
// 定义属性表示票库里的票的数量
private int count = 10;
// 定义属性表示用户买到的是第几张票
private int num = 0;
@Override
public void run() {
while (true) {
synchronized (this) {
// 如果票的数量小于0,就不在卖票
if (count <= 0) {
break;
}
// 每卖一张票,票的总数要-1,卖的是第几张票变量要+1
count--;
num++;
// 买票过程中模拟网速缓慢
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买到了第"
+ num + "张票,还剩余" + count + "张票");
}
}
}
线程安全的类型
为达到安全性和效率的平衡,可以根据实际场景来选择合适的类型
常见类型的对比
Hashtable && HashMap
Hashtable
继承关系:实现了Map接口,Hashtable继承Dictionary类
线程安全,效率较低
键和值都不允许为null
HashMap
继承关系:实现了Map接口,继承AbstractMap类
非线程安全,效率较高
键和值都允许为null
StringBuffer && StringBuilder
前者线程安全,后者非线程安全