package cn.tan.phenomenon;
public class Main {
// 定义一个共享的数据 —— 静态属性的方式来体现
static int r = 0;
// 定义加减的次数
// COUNT 越大,出错的概率越大;COUNT 越小,出错的概率越小
static final int COUNT = 1000_000_00;
// 定义两个线程,分别对 r 进行 加法 + 减法操作
static class Add extends Thread {
@Override
public void run() {
for (int i = 0; i < COUNT; i++) {
r++;
}
}
}
static class Sub extends Thread {
@Override
public void run() {
for (int i = 0; i < COUNT; i++) {
r--;
}
}
}
public static void main(String[] args) throws InterruptedException {
Add add = new Add();
add.start();
Sub sub = new Sub();
sub.start();
add.join();
sub.join();
// 理论上,r 被加了 COUNT 次,也被减了 COUNT 次
// 所以,结果应该是 0
System.out.println(r);
}
}
以上代码是线程安全的吗?
单看代码是没有问题的,可是结果没能达到预期为0的结果
线程不安全的原因
?再看上面的例子,同时操作了一个静态属性r,因此就可能发生错误。
count越大,线程执行需要跨时间片的概率越大,导致中间出错的概率越大。
2.作为程序员如何考虑线程安全的问题
1.尽可能让几个线程之间不做数据共享,各干各的。就不需要考虑线程安全问题了 比如对一个数组的归并排序:4个线程虽然处理的是同一个数组,但提前划好范围,各做各的,就没问题了 2.如果非要有共享操作,尽可能不去修改,而是只读操作 static?final?int?COUNT=;即使多个线程同时使用这个COUNT也无所谓的 3.一定会出现线程问题了,问题的原因从系统角度讲: 1.原子性被破坏了 2.由于内存可见性问题,导致某些线程读取到“脏(dity)” 3.由于代码重排序导致的线程之间关于数据的配合出问题了 原子性就是一个操作要么全部完成,要么都不完成。
?
锁:
synchronized
?关于类名.class的一个知识,每个被加载的类有且仅有一个class对象
?
synchronized修饰两种方法的区别
?互斥现象的产生
?(1)当多个线程都有加锁操作的时候(2)并且申请的是同一把锁的时候,锁的是同一个对象
现代java并发编程之JUC包
?关于lock一些案例的讲解
(1)?
public class Main1 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
lock.lock();
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
t.join();
}
}
主线程加锁,之后子线程想要申请锁申请不到。
(2)
public class Main2 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
lock.lock();
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt(); // 尝试让子线程停下来,但实际会徒劳无功
t.join();
}
}
这里让主线程睡两秒,尝试给子线程一个停止的信号,但是没有一个让他停止的动作,也没有用,锁仍然没有释放,仍然死锁。
(3)
public class Main3 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
} catch (InterruptedException e) {
System.out.println("收到停止信号,停止运行");
}
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();
t.join();
}
}
这个案例和上一个的区别就是尝试让子线程停止,子线程用的是一个允许被中断的锁,这里再调用interrupt()方法就会以一个异常的形式来处理
(4)
package cn.tan.locks;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main4 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
boolean b = lock.tryLock();
if (b == true) {
// 加锁成功了
System.out.println("加锁成功");
System.out.println("子线程进入临界区"); // 理论上,这句代码永远到达不了
} else {
System.out.println("加锁失败");
}
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt(); // 尝试让子线程停下来,但实际会徒劳无功
t.join();
}
}
这一个呢,用了trylock,尝试加锁,但是一直被主线程占用,所以返回一个false,加锁失败。
(5)
package cn.tan.locks;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main5 {
private static final Lock lock = new ReentrantLock();
static class MyThread extends Thread {
@Override
public void run() {
boolean b = false;
try {
b = lock.tryLock(5, TimeUnit.SECONDS);
if (b == true) {
// 加锁成功了
System.out.println("加锁成功");
System.out.println("子线程进入临界区");
} else {
System.out.println("5s 之后加锁失败");
}
} catch (InterruptedException e) {
System.out.println("被人打断了");
}
}
}
public static void main(String[] args) throws InterruptedException {
lock.lock();
MyThread t = new MyThread();
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();
lock.unlock();
t.join();
}
}
?
|