一:什么是线程同步
当多个线程同时操作同一份资源,才有可能出现线程不安全问题,数据出现混乱
可查看此文章最后的小标题,理解线程不安全的代码
二:实现线程同步方式【解决线程不安全】
使用同步锁 synchronized : 有可能出现数据不安全的代码段,让多个线程排队执行
1:synchronized用法
synchronized用法:
(1)修饰普通方法:作用于实例对象【因为普通方法属于对象,所以修饰普通方法时,锁拿当前对象作为锁】
(2)修饰静态方法:作用于类对象【因为静态方法属于类,在多线程操作一个静态方法时,会拿当前静态方法所在的类class作为锁】
(3)修饰代码块:可以指定作用的对象【代码快中锁的对象可以自己设置,一般设置为代码快中操作的相同对象】
注意:
同步的代码范围太大,效率太低
同步的代码范围太小,锁不住,不安全
下面模拟三个用户去购买一百张车票作为案例
1.1:synchronized修饰方法代码Demo
public class Class002_Web12306 implements Runnable{
//共享资源 100张票
int tickets = 100;
@Override
public void run() {
while(true){
if(!buyTicket()){
break;
}
//方法cpu切换调度的可能性
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//一次购票的过程,每一个线程排队购票
//返回值 : true-->继续购买 false->结束购买 synchronized 修饰方法,此方法代码会一起执行完毕才轮到别的线程调用执行
public synchronized boolean buyTicket(){
if(tickets<=0){//<=0表示没有票不在卖了
return false;
}
/*模拟一次购票的时间*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
return true;
}
public static void main(String[] args) {
Class002_Web12306 web = new Class002_Web12306();
//创建线程
Thread th1 = new Thread(web,"张三");
Thread th2 = new Thread(web,"李四");
Thread th3 = new Thread(web,"王五");
//线程开启
th1.start();
th2.start();
th3.start();
}
}
1.2:synchronized修饰块 代码Demo【条件 锁类】
锁类,锁的范围太大
public class Class003_Web12306 implements Runnable{
//共享资源 100张票
int tickets = 100;
@Override
public void run() {
while(true){
//同步块--> 锁类
synchronized (Class003_Web12306.class){
if(tickets<=0){
break;
}
/*模拟一次购票的时间*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
}
//方法cpu切换调度的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class003_Web12306 web = new Class003_Web12306();
//创建线程
//正常情况下没有影响
//Thread th1 = new Thread(web ,"张三");
//Thread th2 = new Thread(web ,"李四");
//Thread th3 = new Thread(web ,"王五");
//如果每个线程都是一个新的对象,互不影响,但是因为锁的是类,所以这三个线程也要等待拿到锁来执行
Thread th1 = new Thread(new Class003_Web12306(),"张三");
Thread th2 = new Thread(new Class003_Web12306(),"李四");
Thread th3 = new Thread(new Class003_Web12306(),"王五");
//线程开启
th1.start();
th2.start();
th3.start();
}
}
1.3:synchronized修饰块 代码Demo【条件 锁对象】
public class Class004_Web12306 implements Runnable{
//共享资源 100张票
int tickets = 100;
@Override
public void run() {
while(true){
//同步块--> 锁类
synchronized (this){
if(tickets<=0){
break;
}
/*模拟一次购票的时间*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--);
}
//方法cpu切换调度的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class004_Web12306 web = new Class004_Web12306();
//创建线程
//正常情况下没有影响
//Thread th1 = new Thread(web ,"张三");
//Thread th2 = new Thread(web ,"李四");
//Thread th3 = new Thread(web ,"王五");
//因为锁的是对象,这三个对象互不影响,所以是一起执行的
Thread th1 = new Thread(new Class004_Web12306(),"张三");
Thread th2 = new Thread(new Class004_Web12306(),"李四");
Thread th3 = new Thread(new Class004_Web12306(),"王五");
//线程开启
th1.start();
th2.start();
th3.start();
}
}
1.4:synchronized修饰块 代码Demo【条件 锁具体资源】
锁资源 : 自定义引用数据类型的对象地址永远不变,如果线程中对象不同,导致资源地址不同,那么锁就不是同一个,就是下面代码,在线程中每个线程有自己的对象,对象中tickets对象的地址肯定不同,所以每个线程都会去买自己有的100张票,如果多个线程只有相同的对象,锁的资源【tickets】地址也是同一个,就会买共有的100张票
public class Class005_Web12306 implements Runnable{
//共享资源 100张票因为锁只能锁对象,所以将此数据封装为引用类型
Tickets tickets = new Tickets();
@Override
public void run() {
while(true){
//同步块--> 锁资源
synchronized (tickets){
if(tickets.num<=0){
break;
}
/*模拟一次购票的时间*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets.num--);
}
//方法cpu切换调度的概率
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Class005_Web12306 web = new Class005_Web12306();
//创建线程
//正常情况下没有影响
//Thread th1 = new Thread(web ,"张三");
//Thread th2 = new Thread(web ,"李四");
//Thread th3 = new Thread(web ,"王五");
//因为锁的是具体引用,所以当线程有自己的对象数据时,互不影响,只有在操作到被锁的数据时候,会等待锁的释放
Thread th1 = new Thread(new Class005_Web12306(),"张三");
Thread th2 = new Thread(new Class005_Web12306(),"李四");
Thread th3 = new Thread(new Class005_Web12306(),"王五");
//线程开启
th1.start();
th2.start();
th3.start();
}
}
//票
class Tickets{
int num = 100;
}