并发:同一个对象被多个线程同时操作
线程同步:处理多线程问题时,多个线程访问同一个对象,并且某个对象还想修改这个线程。这时候就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
锁机制:由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来的访问冲突问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排它锁,就能独占并使用对象资源,其他线程必须等待,使用后释放锁即可。
锁机制存在的问题:
锁机制包含两种用法:synchronized同步方法和synchronized同步块
synchronized同步方法控制对对象的访问,每个对象对应一把锁,每个synchronized同步方法都必须获得调用该方法的锁的对象才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。
缺陷:若将一个大的方法申明为synchronized同步方法将会影响效率
方法里面需要修改的内容才需要锁,锁的太多,浪费资源
买火车票的案例
package com.peng.cny;
//买火车票的案例
public class UnsafeBbuyticket {
public static void main(String[] args) throws InterruptedException {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"车站").start();
new Thread(buyTicket,"高铁").start();
new Thread(buyTicket,"飞机").start();
Thread.sleep(100);
}
}
class BuyTicket implements Runnable{
private int ticketNums=10;
boolean flag = true;//线程的外部停止方式
@Override
public void run() {
while (flag==true){
buy();//调用买票的方法
}
}
private void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
}
}
用了synchronized同步方法后
package com.peng.cny;
//买火车票的案例
public class UnsafeBbuyticket {
public static void main(String[] args) throws InterruptedException {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"车站").start();
new Thread(buyTicket,"高铁").start();
new Thread(buyTicket,"飞机").start();
Thread.sleep(100);
}
}
class BuyTicket implements Runnable{
private int ticketNums=10;
boolean flag = true;//线程的外部停止方式
@Override
public void run() {
while (flag==true){
buy();//调用买票的方法
}
}
//synchronized同步方法,锁的是this,是本身的类的对象
private synchronized void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
}
}
拿票就是有顺序了,也不会重复拿
用synchronized同步方法解决继承Thread类的线程不安全问题
package com.work;
//用synchronized同步方法解决继承Thread类的线程不安全问题
public class work5 {
public static void main(String[] args) {
Window4 window4 = new Window4();
Window4 window1 = new Window4();
Window4 window2 = new Window4();
window1.start();
window2.start();
window4.start();
}
}
class Window4 extends Thread{
private static int tickets = 100;
@Override
public void run() {
while (true) {
buy();
}
}
//在这里不能直接用synchronized同步方法,因为这里锁的对象由三个window4 window1 window2,不是this
//变成静态方法后,这时候的锁(同步监视器)就是唯一的,是Window4.class这个类 (在静态方法里面不能调this)
private static synchronized void buy(){
if (tickets>0){
System.out.println(Thread.currentThread().getName()+" "+tickets);
tickets--;
}
}
}
synchronized同步方法的总结:
同步块:synchronized(Obj){
需要被同步的代码块(操作共享数据的代码即为需要被同步的代码)
}
Obj称为同步监视器
两个人同时去银行取同一个账户的钱案例(实现Runnable接口)
package com.peng.cny;
//两个人同时去银行取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"马化疼的账户");
Drawing you = new Drawing(account,50,"yon");
Drawing tx= new Drawing(account,100,"tx");
you.start();
tx.start();
}
}
//银行账户
class Account{
int money;
String name;
//构造器,初始化变量
public Account(int money,String name) {
this.money = money;//余额
this.name = name;//卡名
}
}
//银行取款
//用继承Thread的方式创建线程,因为不涉及到多个线程操作同个对象
class Drawing extends Thread{
Account 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){
System.out.println(Thread.currentThread().getName()+"取得时候钱不够,取不了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡剩下的钱
account.money = account.money - drawingMoney;
//你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"余额为"+account.money);
// this.getName()= Thread.currentThread().getName()
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
用了synchronized同步块后
package com.peng.cny;
//两个人同时去银行取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"马化疼的账户");
Drawing you = new Drawing(account,50,"yon");
Drawing tx= new Drawing(account,100,"tx");
you.start();
tx.start();
}
}
//银行账户
class Account{
int money;
String name;
//构造器,初始化变量
public Account(int money,String name) {
this.money = money;//余额
this.name = name;//卡名
}
}
//银行取款
//用继承Thread的方式创建线程,因为不涉及到多个线程操作同个对象
class Drawing extends Thread{
Account 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() {
//锁的对象是变化的量,需要增删改查的量
synchronized(account){
// synchronized同步块可以锁任何对象
//判断卡里的钱够不够
if (account.money<drawingMoney){
System.out.println(Thread.currentThread().getName()+"取得时候钱不够,取不了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡剩下的钱
account.money = account.money - drawingMoney;
//你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name+"余额为"+account.money);
// this.getName()= Thread.currentThread().getName()
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
}
不能随便的取钱了,要看余额有多少
package com.work;
//用同步代码块处理继承Thread类的安全问题
public class work4 {
public static void main(String[] args) {
BuyTicket2 ticket2 = new BuyTicket2();
BuyTicket2 ticket3 = new BuyTicket2();
BuyTicket2 ticket4 = new BuyTicket2();
ticket2.setName("线程一");
ticket3.setName("线程二");
ticket4.setName("线程三");
ticket2.start();
ticket3.start();
ticket4.start();
}
}
class BuyTicket2 extends Thread{
private static int tickets = 100;
//因为上面创建了三个对象,所以要让三个线程共享着一个锁obj,就要加static
private static Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized(obj) {
//也可以拿BuyTicket2.class)充当锁
//synchronized(BuyTicket2.class){
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖了第" + tickets + "张票");
tickets--;
} else {
break;
}
// }
}
}
}
}
线程本身不安全案例
package com.peng.cny;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();//建一个数组
for (int i = 0; i <20000 ; i++) {
new Thread(()->{
//把20000条线程添加进去
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());//打印数组大小
}
}
并没有添加20000条线程
用了同步块锁住对象后
package com.peng.cny;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i <20000 ; i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());//打印数组大小
}
}