线程安全-同步方法-Runnable
解决线程安全问题
思路:加锁
SE阶段,提供一个同步锁,关键字synchronized,
它可以给一段代码加锁,同一时刻,只允许它监视的多个线程中的一个线程执行这段代码。
同步的语法格式有两种:
(1)同步方法
【其他修饰符】synchronized 返回值类型 方法名(【形参列表】)【throws 异常列表】{
方法体代码;
}
锁的是方法体代码。
(2)同步代码块
同步锁注意的问题
(1)范围锁太大,不行
会导致其他线程没有机会
(2)范围锁太小,也不行
会导致锁的不彻底
例如:条件判断没锁进去,条件中也用了共享数据
(3)同步锁对象的选择问题
所选择的对象必须各个线程共用。
5、同步方法是如何选择锁对象的呢?
非静态方法:默认的锁对象是this对象
静态方法:默认的锁对象是 当前类对象(当前类的Class对象)
package com.atguigu.safe;
public class TestThreadSafe3 {
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
Thread t1 = new Thread(demo3,"窗口1");
Thread t2 = new Thread(demo3,"窗口2");
Thread t3 = new Thread(demo3,"窗口3");
//3个Thread线程对象,共享同一个Demo3的对象,堆内存中的对象可以被多个线程共享
t1.start();
t2.start();
t3.start();
}
}
/*
总票数假设是10张票,分为3个窗口一起抢
*/
/*
class Demo3 implements Runnable{
private int total = 100;//total如果太小的话可能会出现只有一个窗口卖票
//如果是实例变量,每一个对象都是独立的
//如果只有一个Demo3的对象,那么total就只有一个
@Override
public synchronized void run() { //范围锁太大了
while(total>0){
try {
Thread.sleep(1000);//这句代码的作用是让问题暴露的明显一点
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total +"张票");
}
}
}*/
/*class Demo3 implements Runnable{
private int total = 10;//如果是实例变量,每一个对象都是独立的
//如果只有一个Demo3的对象,那么total就只有一个
@Override
public void run() {
while(total>0){
saleOneTicket();//之前判断完条件,可能因为等锁的过程中,total会被其他线程修改
}
}
//卖一张票
private synchronized void saleOneTicket(){//范围锁太小了,没有包含条件
try {
Thread.sleep(1000);//这句代码的作用是让问题暴露的明显一点
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total +"张票");
}
}*/
class Demo3 implements Runnable{
private int total = 1000;//如果是实例变量,每一个对象都是独立的
//如果只有一个Demo3的对象,那么total就只有一个
@Override
public void run() {
while(total>0){
saleOneTicket();//之前判断完条件,可能因为等锁的过程中,total会被其他线程修改
}
}
//卖一张票
//非静态方法,锁对象是this
//这里的this就是Demo3的对象,从头到尾3个线程使用的是同一个Demo3对象,即锁对象是同一个
private synchronized void saleOneTicket(){//范围锁太小了,没有包含条件
if(total>0) {//total值是多个线程共享的,所以进入同步方法之后,重新判断total值
try {
Thread.sleep(10);//这句代码的作用是让问题暴露的明显一点
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + total + "张票");
}else{
return;
}
}
}
线程安全-同步方法-Thread
静态方法不能调用父类的非静态方法
package com.dudu.day0322;
/**
* @author:嘟嘟
* @date:2022/3/23
*/
public class SafeThreadTest {
public static void main(String[] args) {
Demo1 d1=new Demo1("窗口1");
Demo1 d2=new Demo1("窗口2");
Demo1 d3=new Demo1("窗口3");
d1.start();
d2.start();
d3.start();
}
}
class Demo1 extends Thread{
private static int total=100;
public Demo1(String name) {
super(name);
}
@Override
public void run() {
while (true) {
safe();
}
}
public synchronized static void safe(){
if (total>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩"+total+"张票");
}else{
return;
}
}
}
线程安全-同步代码块-Runnable
同步的语法格式:同步代码块
synchronized(锁对象){
要加锁的代码
}
注意的问题:
范围不能太大、太小。
锁对象的选择必须是同一个锁对象
package com.dudu.day0322;
/**
* @author:嘟嘟
* @date:2022/3/23
*/
public class SafeTest1 {
public static void main(String[] args) {
Demo2 demo = new Demo2();
Thread t1=new Thread(demo,"窗口1");
Thread t2=new Thread(demo,"窗口2");
Thread t3=new Thread(demo,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Demo2 implements Runnable{
private int total=100;
@Override
public void run() {
while (total>0){
synchronized(this){
if (total>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩下"+total+"张票");
}
}
}
}
}
线程安全-同步代码块-Thread
package com.dudu.day0322;
/**
* @author:嘟嘟
* @date:2022/3/23
*/
public class SafeThreadTest {
public static void main(String[] args) {
Demo1 d1=new Demo1("窗口1");
Demo1 d2=new Demo1("窗口2");
Demo1 d3=new Demo1("窗口3");
d1.start();
d2.start();
d3.start();
}
}
class Demo1 extends Thread{
private static int total=100;
public Demo1(String name) {
super(name);
}
@Override
public void run() {
while (total>0) {
//synchronized(this)这里的this指的是Demo1对象、有三个
synchronized(this.getClass()){
//this.getClass()、对应的类是同一个
if (total>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
total--;
System.out.println(Thread.currentThread().getName()+"卖了一张票、还剩"+total+"张票");
}
}
}
}
}