- 定义:多个线程控制同一个对象
- 为什么要线程同步:java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。例子:买车票,银行取钱,线程列表等等
- 买车票:(未使用线程同步)
package com.Thread.demo1;
//多线程同时控制一个对象
//买火车票的例子
//发现问题: 多线程控制一个对象,不安全,出现数据紊乱的情况
public class threadTest3 implements Runnable{
private int tickerNumber = 10;
@Override
public void run() {
while (tickerNumber>=1){
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->得到第"+tickerNumber+"张票");
tickerNumber--;
}
}
public static void main(String[] args) {
threadTest3 threadTest3 = new threadTest3();
new Thread(threadTest3,"小盆友").start();
new Thread(threadTest3,"黄牛").start();
new Thread(threadTest3,"农民工").start();
}
}
输出结果:
- 银行取钱(未使用线程同步)
package com.Thread.syn;
//不安全的银行
//两个人去银行取钱
import com.oop.demo6.Action;
public class unsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"账户");
Drawing me = new Drawing(account,50,"我");
Drawing you = new Drawing(account,100,"你");
me.start();
you.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money,String name){
this.money = money;
this.name = name;
}
}
//模拟取款
class Drawing extends Thread{
//账户
Account account;
//取了多少钱
int drawingMoney;
//现在手里有多少钱
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//判断有没有钱
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"取不了,没钱了");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account.money = account.money - drawingMoney;
//现在手里多少钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"余额有"+account.money+"万");
//这里的Thread.currentThread().getName()等价于this.name
System.out.println(this.getName()+"有"+nowMoney+"万");
}
}
输出结果:
- 线程列表(未使用线程同步)
package com.Thread.syn;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class unsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
输出结果:
- 同步方法:
-
即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
-
同步代码块即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
-
- 坏处:开销大。
- 买车票(使用同步方法)
package com.Thread.syn;
//安全的买票
//synchronized 同步方法
public class safeBuyTicket {
public static void main(String[] args) {
BuyTicket1 buyTicket = new BuyTicket1();
new Thread(buyTicket,"小龙").start();
new Thread(buyTicket,"小虎").start();
new Thread(buyTicket,"小貂").start();
}
}
//买票
class BuyTicket1 implements Runnable{
private int ticketNums = 10;
boolean flag = true;//标志位
@Override
public void run() {
while (flag){//买票
buy();
}
}
//买票
//用synchronized ()锁住的是 this
private synchronized void buy(){
//先判断是否还有票
if (ticketNums<=0){
flag = false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就输出
System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums--+"张票");
}
}
输出结果:
- 银行取钱(使用同步方法)
package com.Thread.syn;
//安全的银行
//两个人去银行取钱,用synchronized ()锁住进行增删改的对象,确保安全
import com.oop.demo6.Action;
public class safeBank {
public static void main(String[] args) {
Account1 account = new Account1(100,"账户");
Drawing1 me = new Drawing1(account,50,"我");
Drawing1 you = new Drawing1(account,100,"你");
me.start();
you.start();
}
}
//账户
class Account1{
int money;
String name;
public Account1(int money,String name){
this.money = money;
this.name = name;
}
}
//模拟取款
class Drawing1 extends Thread{
//账户
Account1 account;
//取了多少钱
int drawingMoney;
//现在手里有多少钱
int nowMoney;
public Drawing1(Account1 account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//用synchronized ()代码块来锁住要进行增删改的对象
@Override
public void run() {
synchronized (account){//同步方法块。
//判断有没有钱
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"取不了,没钱了");
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account.money = account.money - drawingMoney;
//现在手里多少钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"余额有"+account.money+"万");
//这里的Thread.currentThread().getName()等价于this.name
System.out.println(this.getName()+"有"+nowMoney+"万");
}
}
}
输出结果:
- 线程列表(使用同步方法)
package com.Thread.syn;
import java.util.ArrayList;
import java.util.List;
public class safeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){//()内放的是对象 该对象正常是增删改的对象。
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
输出结果: