线程安全问题
导致安全问题的出现的原因:
-
多个线程访问出现延迟。
-
线程随机性。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大。
同步(synchronized)
格式:
synchronized(对象)
{
需要同步的代码;
}
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
同步的前提:
-
同步需要两个或者两个以上的线程。
-
多个线程使用的是同一个锁。
未满足这两个条件,不能称其为同步。
同步的弊端:
-
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形 中会降低程序的运行效率。
如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
举例:
/*
通过分析抢票模拟程序发现,打印出0,-1,-2等错票。
多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
synchronized(对象)
{
需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
火车上的卫生间---经典。
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,
*/
public class ThicketDemo2 implements Runnable{
private static int tick = 100;
Object obj = new Object(); // 创建一个对象,确保使用的是同一个对象
@Override
public void run() {
while(true){
// 同步代码块,等这个线程执行完后
synchronized (obj) {
if(tick>0){
System.out.println(Thread.currentThread().getName()+"-->"+tick--);
}else{
break;
}
}
}
}
public static void main(String[] args) {
ThicketDemo2 tt = new ThicketDemo2();
System.out.println("开启售票。。。");
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
}
举例2:
/*
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次。
目的:该程序是否有安全问题,如果有,如何解决?
如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
*/
class Bank{
private int sum;
Object obj = new Object();
public void add(int n){
//同步代码块
synchronized (obj) {
sum += n;
System.out.println("sum="+sum);
}
}
}
class Cus implements Runnable{
private Bank b = new Bank();
@Override
public void run() {
for(int i = 0;i<3;i++){
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus cus = new Cus();
new Thread(cus).start();
new Thread(cus).start();
new Thread(cus).start();
}
}
同步函数
格式:
在函数上加上synchronized修饰符即可。
思考:同步函数用的是哪个锁呢?
/**
* 同步函数用的是哪一个锁呢??
* 函数需要被对象调用。那么函数都有所属对象的引用。就是this
* 所以同步函数的锁市this(因为他们共用的是一个对象tt)
*/
public class ThicketDemo3 implements Runnable{
private static int tick = 100;
@Override
public void run() {
while(true){
this.show();
}
}
public static void main(String[] args) {
ThicketDemo3 tt = new ThicketDemo3();
System.out.println("开启售票。。。");
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
public synchronized void show(){
if(tick>0){
System.out.println(Thread.currentThread().getName()+"-->"+tick--);
}
}
}
class Bank{
private int sum;
//同步函数
public synchronized void add(int n){
sum += n;
System.out.println("sum="+sum);
}
}
class Cus implements Runnable{
private Bank b = new Bank();
@Override
public void run() {
for(int i = 0;i<3;i++){
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus cus = new Cus();
new Thread(cus).start();
new Thread(cus).start();
new Thread(cus).start();
}
}
同步函数加上static,使用的是哪个锁??
/**
* 如果同步函数被静态修饰后,使用的锁是什么?
* 通过验证发现不在是this。因为静态方法中也不可以定义this
* 静态进内存时,内存中没有本类对象,但一定有该类对象的字节码对象
* 类名.class 该对象的类型时Class
*
*
* 静态的同步方法使用锁是类的字节码文件对象, 类目.class
*/
public class ThicketDemo4 implements Runnable{
private static int tick = 100;
@Override
public void run() {
while(true){
show();
}
}
public static void main(String[] args) {
ThicketDemo4 tt = new ThicketDemo4();
System.out.println("开启售票。。。");
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
public static synchronized void show(){
if(tick>0){
System.out.println(Thread.currentThread().getName()+"-->"+tick--);
}
}
}
死锁(重要,面试会考)
/**
* 死锁:线程1获取了A锁,线程2获取了B锁,这是线程1需要获取B锁,线程2需要获取A锁,此时就处于僵持状态。
*
* @author hecha
*
*/
class Test implements Runnable{
private static Object A = new Object();
private static Object B = new Object();
private boolean flag;
public Test(boolean flag){
this.flag = flag;
}
@Override
public void run() {
if(flag){
synchronized (A) {
System.out.println(Thread.currentThread().getName()+"-->A");
synchronized (B) {
System.out.println(Thread.currentThread().getName()+"-->B");
}
}
}else{
synchronized (B) {
System.out.println(Thread.currentThread().getName()+"-->B");
synchronized (A) {
System.out.println(Thread.currentThread().getName()+"-->A");
}
}
}
}
}
public class DeadLockTest {
public static void main(String[] args) {
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}