目录
一、线程的状态
1.new
示例代码:
public static void main(String[] args) {
Thread t = new Thread(() -> {
});
System.out.println(t.getState());
}
运行结果:
2.terminated
示例代码:
public static void main(String[] args) {
Thread t = new Thread(() -> {
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getState());
}
运行结果:
需要注意的是,上述两个状态是Java自身定义的,和操作系统中的PCB状态没有关系。
3.runnable
示例代码:
public static void main(String[] args) {
Thread t = new Thread(() -> {
});
t.start();
System.out.println(t.getState());
}
运行结果:
4.timed_waiting
代码示例:
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
Thread.sleep(500);
System.out.println(t.getState());
}
运行结果:
5.blocked
6.waiting
小结
我们可以将这几种状态转移的关系简单地画下面这个图:
二、线程安全
1.线程安全的概念
2.线程不安全的原因
说到线程不安全的原因就不得不提原子性操作,我们先来了解一下什么是原子性操作:
我们来看一段代码:
class count {
public static int num;
public void increase() {
num++;
}
}
class Main2 {
public static void main(String[] args) throws InterruptedException {
count c = new count();
Thread t1 = new Thread(() -> {
Object o = new Object();
for(int i = 0; i < 50000; i++) {
c.increase();
}
});
Thread t2 = new Thread(() -> {
Object o = new Object();
for(int i = 0; i < 50000; i++) {
c.increase();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.num);
}
}
我们定义了两个线程,每一个线程都会使num自增50000次,那么按照代码的逻辑最后num的值应该是100000,那么实际结果真的是这样子吗?
执行结果:
我们会发现 结果跟我们认为的是有很大差距的,那么这种结果的原因是什么呢?我们来分析一下。
除了 非原子性操作带来的线程不安全之外,线程的抢占式执行才是线程不安全的万恶之源。
而在上述代码之中,我们针对自增的是同一个对象,如果我们可以使两个线程去执行不同变量的自增最后将结果加起来,一样可以避免出现线程不安全的问题。
当然,我们也可以使用synchronized关键字,对代码的某些关键操作上锁,将非原子性操作打包成一个原子性的操作
优化后的代码:
class count {
public static int num;
synchronized public void increase() {//加锁
num++;
}
}
class Main2 {
public static void main(String[] args) throws InterruptedException {
count c = new count();
Thread t1 = new Thread(() -> {
Object o = new Object();
for(int i = 0; i < 50000; i++) {
c.increase();
}
});
Thread t2 = new Thread(() -> {
Object o = new Object();
for(int i = 0; i < 50000; i++) {
c.increase();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count.num);
}
}
此外,内存可见性也会导致线程安全
代码示例:
public static int a = 1;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while(a == 1) {
}
System.out.println("循环结束");
});
t.start();
Scanner scan = new Scanner(System.in);
System.out.println("输入一个数字:");
a = scan.nextInt();
System.out.println("main结束!");
}
执行结果:
我们发现,在我们修改了a的值之后,直到主线程结束t线程也没有结束,这就是内存可见性导致的线程不安全
修改后:
volatile public static int a = 1;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while(a == 1) {
}
System.out.println("循环结束");
});
t.start();
Scanner scan = new Scanner(System.in);
System.out.println("输入一个数字:");
a = scan.nextInt();
System.out.println("main结束!");
}
此外,volatile关键字还可以防止指令重排序。
以上就是今天的全部内容了,喜欢的话就点个赞吧!