多线程访问共享变量的时候会出现线程安全的问题
解决线程安全的问题:线程同步(同步代码块、同步方法、同步锁)
1.同步代码块
synchronized(同步监视器){
//需要访问的共享数据
}
同步监视器 : 俗称“锁”。可以使用任何对象充当。但是必须确定多个线程持有同一把锁(同一个对象)
2.同步方法
同步方法: ---- 隐式的锁 : this
----如果同步方法被静态(static)修饰后,隐式的锁是该类所属的字节码文件xxx.class(因为被static修饰,没有对象可调用此方法)
public synchronized void show(){
//需要访问的共享数据
}
3.同步锁
public class MyRunnable implements Runnable {
//static修饰 保证是共享资源 多个线程构造器传入同一个此对象则不必是static也可以
public static int tick = 100;
//创建一个锁 static修饰 保证是一个锁 多个线程构造器传入同一个此对象则不必是static也可以
public static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//上锁
lock.lock();
try {
if (tick > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
tick--;
System.out.println(Thread.currentThread().getName() + "完成售票,余票为 :" + tick);
}
} finally {
//必须手动解锁
lock.unlock();
}
}
}
}
public class TestLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Thread thread1 = new Thread(new MyRunnable(),"aa");
thread1.start();
Thread thread2 = new Thread(new MyRunnable(),"bb");
thread2.start();
}
}
public class TestThreadDeadLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable = new MyRunnable();
//下面两个线程构造器都传入同一个myRunnable 则不必用上面说的static修饰共享变量
Thread thread1 = new Thread(myRunnable, "aa");
thread1.start();
Thread thread2 = new Thread(myRunnable, "bb");
thread2.start();
}
}
打印结果
aa完成售票,余票为 :99
aa完成售票,余票为 :98
bb完成售票,余票为 :97
bb完成售票,余票为 :96
aa完成售票,余票为 :95
aa完成售票,余票为 :94
aa完成售票,余票为 :93
aa完成售票,余票为 :92
bb完成售票,余票为 :91
aa完成售票,余票为 :90
bb完成售票,余票为 :89
aa完成售票,余票为 :88
bb完成售票,余票为 :87
bb完成售票,余票为 :86
bb完成售票,余票为 :85
bb完成售票,余票为 :84
aa完成售票,余票为 :83
aa完成售票,余票为 :82
bb完成售票,余票为 :81
bb完成售票,余票为 :80
bb完成售票,余票为 :79
bb完成售票,余票为 :78
bb完成售票,余票为 :77
bb完成售票,余票为 :76
aa完成售票,余票为 :75
aa完成售票,余票为 :74
aa完成售票,余票为 :73
aa完成售票,余票为 :72
aa完成售票,余票为 :71
aa完成售票,余票为 :70
aa完成售票,余票为 :69
aa完成售票,余票为 :68
aa完成售票,余票为 :67
aa完成售票,余票为 :66
aa完成售票,余票为 :65
aa完成售票,余票为 :64
aa完成售票,余票为 :63
aa完成售票,余票为 :62
aa完成售票,余票为 :61
aa完成售票,余票为 :60
aa完成售票,余票为 :59
aa完成售票,余票为 :58
aa完成售票,余票为 :57
bb完成售票,余票为 :56
bb完成售票,余票为 :55
bb完成售票,余票为 :54
bb完成售票,余票为 :53
bb完成售票,余票为 :52
bb完成售票,余票为 :51
bb完成售票,余票为 :50
bb完成售票,余票为 :49
bb完成售票,余票为 :48
bb完成售票,余票为 :47
bb完成售票,余票为 :46
bb完成售票,余票为 :45
bb完成售票,余票为 :44
bb完成售票,余票为 :43
bb完成售票,余票为 :42
bb完成售票,余票为 :41
bb完成售票,余票为 :40
bb完成售票,余票为 :39
bb完成售票,余票为 :38
bb完成售票,余票为 :37
bb完成售票,余票为 :36
bb完成售票,余票为 :35
bb完成售票,余票为 :34
aa完成售票,余票为 :33
aa完成售票,余票为 :32
aa完成售票,余票为 :31
aa完成售票,余票为 :30
aa完成售票,余票为 :29
aa完成售票,余票为 :28
bb完成售票,余票为 :27
bb完成售票,余票为 :26
bb完成售票,余票为 :25
bb完成售票,余票为 :24
bb完成售票,余票为 :23
aa完成售票,余票为 :22
aa完成售票,余票为 :21
aa完成售票,余票为 :20
aa完成售票,余票为 :19
aa完成售票,余票为 :18
aa完成售票,余票为 :17
aa完成售票,余票为 :16
aa完成售票,余票为 :15
aa完成售票,余票为 :14
aa完成售票,余票为 :13
aa完成售票,余票为 :12
aa完成售票,余票为 :11
aa完成售票,余票为 :10
aa完成售票,余票为 :9
aa完成售票,余票为 :8
aa完成售票,余票为 :7
aa完成售票,余票为 :6
aa完成售票,余票为 :5
aa完成售票,余票为 :4
bb完成售票,余票为 :3
bb完成售票,余票为 :2
bb完成售票,余票为 :1
bb完成售票,余票为 :0
4.读写分离锁:ReentrantReadWriteLock(读读共享、写写互斥、读写互斥)
public class UseReentrantReadWriteLock {
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private ReadLock readLock = rwLock.readLock();
private WriteLock writeLock = rwLock.writeLock();
public void read(){
try {
readLock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public void write(){
try {
writeLock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
} catch (Exception e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
public static void main(String[] args) {
final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
urrw.read();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
urrw.read();
}
}, "t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
urrw.write();
}
}, "t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
urrw.write();
}
}, "t4");
// t1.start();
// t2.start();
// t1.start(); // R
// t3.start(); // W
t3.start();
t4.start();
}
}
线程的死锁 : 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
匿名内部类写一个死锁
public class TestThreadDeadLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//锁1 内部类如果引用外部类的变,则该变量必须为final,这是规定
final Object obj1 = new Object();
//锁2
final Object obj2 = new Object();
//线程1
new Thread(){
public void run(){
synchronized (obj1) {
System.out.println("获取资源1,等待资源2......");
try {
//线程1获取锁1后 睡一会(此时线程1占用锁1),等待线程2获取锁2(线程2占用锁2)
Thread.sleep(200);
} catch (InterruptedException e) {
}
synchronized (obj2) {
System.out.println("--------------------------------");
}
}
}
}.start();
// Thread.sleep(5000);
//线程2
new Thread() {
public void run() {
synchronized (obj2) {
System.out.println("获取资源2,等待资源1......");
synchronized (obj1) {
System.out.println("****************************************");
}
}
}
}.start();
}
}
获取资源1,等待资源2......
获取资源2,等待资源1......
代码写一个死锁
public class MyRunnable implements Runnable {
//锁1 必须是static修饰的 代表同一把锁
public static Object obj1 = new Object();
//锁2
public static Object obj2 = new Object();
public boolean flag = true;
@Override
public void run() {
if(flag){
synchronized (obj1) {
System.out.println("获取资源1,等待资源2......");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("--------------------------");
}
}
}else{
synchronized (obj2) {
System.out.println("获取资源2,等待资源1......");
synchronized (obj1) {
System.out.println("**************************");
}
}
}
}
}
public class TestThreadDeadLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable1 = new MyRunnable();
Thread thread1 = new Thread(myRunnable1);
thread1.start();
MyRunnable myRunnable2 = new MyRunnable();
myRunnable2.flag = false;
Thread thread2 = new Thread(myRunnable2);
thread2.start();
}
}
获取资源1,等待资源2......
获取资源2,等待资源1......