多个线程操作同一个数据的情况下,线程不安全了!

package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}package study.thread;
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeImpl threadSafe = new ThreadSafeImpl();
//多线程干一件事
new Thread(threadSafe).start();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}(发现票卖重了,而且卖了不存在的票,这就有问题了!)
售票员Thread-2正在卖第100张票……
售票员Thread-0正在卖第100张票……
售票员Thread-1正在卖第100张票……
...
售票员Thread-2正在卖第1张票……
售票员Thread-0正在卖第1张票……
售票员Thread-1正在卖第-1张票……
①同步代码块;
②同步方法;
③锁机制;
synchronized(锁对象){
可能出现线程安全问题的代码(从访问到共享数据的代码开始)
}前面的线程开始执行后回去拿取堆内存中的锁对象,后面的线程开始执行后再去拿锁对象就拿不到了,所以无法继续执行,需要等待前面的线程执行完毕,归还锁对象,后面的线程才能拿到锁对象,继续执行;
①通过代码块中的锁对象,可以使用任意的对象;
②但必须保证多个线程使用的所对象是同一个;
③所对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行;
加入同步代码块后的多线程类:
package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
private final Object object = new Object();
@Override
public void run() {
while (true){
synchronized (object){
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}
}测试类:
package study.thread;
public class ThreadSafeTest {
public static void main(String[] args) {
ThreadSafeImpl threadSafe = new ThreadSafeImpl();
//多线程干一件事
new Thread(threadSafe).start();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}运行结果(截取部分):
售票员Thread-0正在卖第100张票……
售票员Thread-0正在卖第99张票……
售票员Thread-2正在卖第98张票……
售票员Thread-1正在卖第97张票……
售票员Thread-1正在卖第96张票……
售票员Thread-1正在卖第95张票……
①把访问了共享数据的代码抽取出来,放到一个方法中;
②在方法上添加synchronized修饰符;
访问修饰符 synchronized 返回值类型 方法名(参数列表){
//方法体
}package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
printMessage();
}
private synchronized void printMessage(){
while (true){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
}
}
}但经测试发现,此方法一个会出现一个线程将票卖完的情况,我自己猜测也许是因为while循环放进了锁住的方法中。
package study.thread;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true){
printMessage();
if(ticket==0){
break;
}
}
}
private synchronized void printMessage(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}
}
}这样写确实避免了上述情况。
同步方法也会把方法内部的代码锁住,只让一个线程执行,实际上所的对象是new RunnableImpl(),也就是this(自身);
静态同步方法就是在一般的同步方法synchronized前加上static;
此时的锁对象不是this(本身),而是本类的class属性-->class文件对象(反射);
Lock接口实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作;
①在成员位置创建一个ReentrantLock(可重入锁);
②在有可能出现安全问题的代码前,调用获取锁的方法(闭锁);
③在有可能出现安全问题的代码后,调用释放锁的方法(开锁);
package study.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
//1、在成员位置创建一个
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
//2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
lock.lock();
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
}else {
break;
}
//3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
lock.unlock();
}
}
}package study.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafeImpl implements Runnable {
private int ticket = 100;
//1、在成员位置创建一个
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
//2、在有可能出现安全问题的代码前,调用获取锁的方法(闭锁)
lock.lock();
if(ticket>0){
try {
Thread.sleep(10);
System.out.println("售票员"+Thread.currentThread().getName()+"正在卖第"+ticket+"张票……");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3、在有可能出现安全问题的代码后,调用释放锁的方法(开锁)
lock.unlock();
}
}else {
break;
}
}
}
}多个线程同时站有一些共享资源,并且相互等待其他线程占有的资源才能运行,而这导致两个或多个线程都在等待对方释放资源,都停止运行的情形;
某一个同步代码块同时拥有“两个以上对象的锁”时,就可能发生“死锁”;
又两个小朋友,小明和小强,小明有玩具汽车,但是小明想要小强的玩具枪,相反小强有玩具枪却想要小明的玩具汽车,双方都说如果你先把你的玩具给我,我就给你,所以两个小朋友都在等对方把玩具给自己,这就僵持住了,在程序中也就是“死锁”;
package com.zb.thread;
import lombok.SneakyThrows;
//测试死锁
public class TestDeadlock {
public static void main(String[] args) {
Object gan = new Object();
Object car = new Object();
new Thread(new Play("小明",gan,car)).start();
new Thread(new Play("小强",gan,car)).start();
}
}
class Play implements Runnable{
private final String name;
private final Object gan;
private final Object car;
public Play(String name, Object gan, Object car) {
this.name = name;
this.gan = gan;
this.car = car;
}
@SneakyThrows
@Override
public void run() {
if("小明".equals(name)){
synchronized (car){//小明持有玩具车
System.out.println("小明持有玩具车!");
System.out.println("小明想要玩具枪!");
synchronized (gan){
System.out.println("小明获得了玩具枪!");
}
}
}else if("小强".equals(name)){
synchronized (gan){//小强持有玩具枪
System.out.println("小强持有玩具枪!");
System.out.println("小强想要玩具车!");
synchronized (car){
System.out.println("小明获得了玩具枪!");
}
}
}
}
}小明持有玩具车!
小明想要玩具枪!
小强持有玩具枪!
小强想要玩具车!package com.zb.thread;
import lombok.SneakyThrows;
//测试死锁
public class TestDeadlock {
public static void main(String[] args) {
Object gan = new Object();
Object car = new Object();
new Thread(new Play("小明",gan,car)).start();
new Thread(new Play("小强",gan,car)).start();
}
}
class Play implements Runnable{
private final String name;
private final Object gan;
private final Object car;
public Play(String name, Object gan, Object car) {
this.name = name;
this.gan = gan;
this.car = car;
}
@SneakyThrows
@Override
public void run() {
if("小明".equals(name)){
synchronized (car){//小明持有玩具车
System.out.println("小明持有玩具车!");
System.out.println("小明想要玩具枪!");
}
Thread.sleep(1000);
synchronized (gan){
System.out.println("小明获得了玩具枪!");
}
}else if("小强".equals(name)){
synchronized (gan){//小强持有玩具枪
System.out.println("小强持有玩具枪!");
System.out.println("小强想要玩具车!");
}
synchronized (car){
System.out.println("小明获得了玩具枪!");
}
}
}
}小明持有玩具车!
小明想要玩具枪!
小强持有玩具枪!
小强想要玩具车!
小明获得了玩具枪!
小明获得了玩具枪!