写在前面:最近在学习多线程的部分,尝试写一写博客,整理自己的思路,有理解不到位的欢迎指正,共同进步。
线程的创建
线程的创建有两种方式:
1、直接new Thread类
2、实现Runnable接口
Thread thread = new Thread() {
@Override
public void run() {
// 保证线程一直在运行
while (true) {
try {
// 睡1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread:" + Thread.currentThread().getName());
}
}
};
thread.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Runnable:" + Thread.currentThread().getName());
}
}
});
thread2.start();
代码会不停地打印两个线程的名称
thread:Thread-0
Runnable:Thread-1
Runnable:Thread-1
thread:Thread-0
思考题
如果new Thread的同时又实现了Runnable接口,分别重写不同run方法,会执行哪一个
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("runnable3");
}
};
Thread thread3 = new Thread(runnable) {
@Override
public void run() {
System.out.println("thread3");
}
};
thread3.start();
答案
Runnable相当于是Thread的父类,父类和子类都实现了run方法,子类会覆盖父类,所以输出是
thread3
传统的定时器
Java本身的定时器Timer通过新建一个定时器,实现一个定时任务来完成。
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println("bombing");
}
}, 3000, 5000); //3s后爆炸,每隔5s爆炸
//通过循环打印当前时间来观察爆炸时间
while(true) {
System.out.print(new Date().getSeconds() + " ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
输入如下:
9 10 11 bombing
12 13 14 15 16 bombing
17 18 19 20 21 bombing
思考题
如果想要实现定时器,间隔2s和4s交替爆炸,应该怎样做
答案
常规定时器执行间隔时间在创建时已经设置好了,不符合要求。考虑在一个定时器内嵌套一个另一个定时器,这时怎样控制间隔时间?利用静态属性来控制,无论新建多少对象,静态属性始终只有一个。
代码如下:
public class MyTimerTask extends TimerTask{
private static int num = 1; // 静态属性,一个类只有一个值
@Override
public void run() {
System.out.println("num=" + num++ + " " + new Date().getSeconds() + " bombing ");
int count = num % 2;
try {
Thread.sleep(count);
// 嵌套一个定时任务
new Timer().schedule(new MyTimerTask(), count * 2000 + 2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 主函数新建一个定时任务即可
public static void main(String[] args) {
new Timer().schedule(new MyTimerTask(), 2000);
}
输出为:
num=1 14 bombing
num=2 16 bombing
num=3 20 bombing
num=4 22 bombing
num=5 26 bombing
num=6 28 bombing
线程互斥
下面代码实现了一个简单的字符串输出功能,一直不停的输出“zhangSan”和“liSi”
public class SynchronizedThread {
public static void main(String[] args) {
while (true) {
new Thread() {
@Override
public void run() {
output("zhangSan");
}
}.start();
new Thread() {
@Override
public void run() {
output("liSi");
}
}.start();
}
}
public static void output(String str) {
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i));
}
System.out.println();
}
}
从输出结果中发现了异常的结果
zhangSaliSi
zhanglliSi
zhangSan
输出的顺序发生了混乱,什么导致了这种情况呢?
一个线程正在输出的过程中,资源被另一个线程占用导致当前线程等待,于是一个字符串的打印一部分又开始打印另一个字符串,便出现了乱码。如果涉及到银行账户余额的变化时,一个线程对余额的增加还没处理完成,另一个线程对余额进行扣除,就会出现严重的资金问题。所以需要线程互斥。在这里我们用加锁的方式来保证线程的原子性。
可以对类进行加锁,也可以对代码块加锁,关键字synchronized。
public static synchronized void output2(String str) {
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i));
}
System.out.println();
}
public static void output3(String str) {
synchronized (SynchronizedThread.class) {
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i));
}
System.out.println();
}
}