目录
一:线程安全判断依据
1、是否存在多线程环境
2、是否存在共享数据/共享变量
3、是否有多条语句操作着共享数据/共享变量
二:解决线程安全问题实现(同步代码块)
1:格式
synchronized(锁对象){//对象要先创建可以是自定义类对象也可以是object类对象
synchronized加在方法上时在修饰符和返回值之间
需要同步的代码;
}
2:同步代码块的锁对象是谁?
任意对象
package day32;
public class SellTicketThread implements Runnable{//自定义类实现Runnable接口
int tickets=100;//定义票总量
boolean b =true;//一个布尔标识来控制程序的结束
Object o = new Object();//定义一个对象
@Override
public void run() {
while(b){
synchronized (o){//同步代码块 synchronized中传入o对象
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张票");//打印窗口名字以及正在卖的票
}
else{
b=false;
}
}
}
}
}
3:同步方法的时候,锁对象又是谁呢?
this
package day32;
public class SellTicketThread implements Runnable{
int tickets=100;
boolean b =true;
@Override
public void run() {
sellTickets();//调用方法
}
public void sellTickets(){
while(b){
synchronized (this){//传入this对象
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张票");
}
else{
b=false;
}
}
}
}
}
4:静态同步方法,锁对象是谁?
该静态同步方法所属那个类的字节码文件
字节码文件也属于一种对象。静态方法属于那个类的字节码文件即 类名.class
package day32;
public class SellTicketThread implements Runnable{
static int tickets=100;//变量加static因为静态的方法只能调用静态的变量
static boolean b =true;
@Override
public void run() {
sellTickets();//调用方法
}
public static void sellTickets(){
while(b){
synchronized (SellTicketThread.class){//传入当前类节码文件
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张票");
}
else{
b=false;
}
}
}
}
}
三:卖票程序
1:分析
卖票程序是否满足以上线程安全条件
1、是否存在多线程环境 是,由于有三个线程模拟三个窗口
2、是否存在共享数据/共享变量 是 共享数据是100张票
3、是否有多条语句操作着共享数据/共享变量 是
2:第一种实现多线程方法写卖票程序
package day32;
public class SellTicketThread1 extends Thread {
static int tickets =100;//定义票数
boolean b =true;//定义标识
Object o =new Object();
@Override
//重新run方法
public void run() {
while(b){
synchronized (o){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张票");
}
else{//如票数小于等于0,标识为false退出循环
b =false;
}
}
}
}
}
package day32;
public class SellTickets2 {
public static void main(String[] args) {
//创建自定义类对象
SellTicketThread1 sellTicketThread = new SellTicketThread1();
SellTicketThread1 sellTicketThread1 = new SellTicketThread1();
SellTicketThread1 sellTicketThread2 = new SellTicketThread1();
//他们设置名字
sellTicketThread.setName("窗口一");
sellTicketThread1.setName("窗口二");
sellTicketThread2.setName("窗口三");
//启动线程
sellTicketThread.start();
sellTicketThread1.start();
sellTicketThread2.start();
}
}
3: 第二种实现多线程方法写卖票程序
package day32;
public class SellTicketThread implements Runnable{
int tickets=100;//定义票数
boolean b =true;//定义标识
Object o =new Object();
@Override
//重新run方法
public void run() {
while(b){
synchronized (o){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在卖第"+tickets--+"张票");
}
else{//如票数小于等于0,标识为false退出循环
b =false;
}
}
}
}
}
package day32;
public class SellTickets {
public static void main(String[] args) {
SellTicketThread sellTicketThread = new SellTicketThread();//创建继承Runnable类对象
//将继承Runnable类对象传入Thread构造方法中创建三个线程
Thread w1 = new Thread(sellTicketThread, "窗口一");
Thread w2 = new Thread(sellTicketThread, "窗口二");
Thread w3 = new Thread(sellTicketThread, "窗口三");
//启动线程
w1.start();
w2.start();
w3.start();
}
}
4:注意事项
当有共享变量用继承thread类时变量要有public satatic修饰
有共享变量时最好用继承接口方式
因为锁是多个线程公用一个是唯一的