线程同步
多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候就需要线程同步;
线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池,形成队列,等待前面线程使用完毕,下一个线程再使用;
形成条件:队列+锁(synchronized)
线程不安全案例
多人买票是非线程安全:
//多人买票是非线程安全
public class BuyTickets{
public static void main(String[] args) {
BuyTicketsThread ticketApp = new BuyTicketsThread();
new Thread(ticketApp,"张三").start();
new Thread(ticketApp,"李四").start();
new Thread(ticketApp,"王五").start();
}
}
class BuyTicketsThread implements Runnable{
int ticketNum = 10;
@Override
public void run() {
while(true){
buy();
if(ticketNum<=0)
break;
}
}
void buy(){
if(ticketNum <= 0)
return;
System.out.println(Thread.currentThread().getName()+"获得"+ticketNum);
ticketNum--;
}
}
多人同时在一个账户上取钱为非线程安全:
//多人同时在一个账户上取钱为非线程安全
public class WithdrawMoney {
public static void main(String[] args) {
Account account = new Account(100, "XX银行卡");
WithdrawMoneyThread zhangsan = new WithdrawMoneyThread(account, 1, "张三");
WithdrawMoneyThread wife = new WithdrawMoneyThread(account, 100, "张三的媳妇儿");
zhangsan.start();
wife.start();
}
}
class Account{
int balance;//余额
String name;//取款人姓名
public Account(int balance, String name) {
this.balance = balance;
this.name = name;
}
}
class WithdrawMoneyThread extends Thread{
Account account; //账户
int drawing; //要取的钱
int cash; //手中的钱
public WithdrawMoneyThread(Account account, int drawing, String name) {
super(name);
this.account = account;
this.drawing = drawing;
}
@Override
public void run() {
if(account.balance < drawing){
System.out.println(this.getName()+"取钱时钱不够了,取不了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//余额 = 余额 - 要取的钱
account.balance = account.balance - drawing;
//手中的钱 = 手中的钱 + 要取的钱
cash = cash + drawing;
System.out.println("余额为"+account.balance);
System.out.println(this.getName() + "手中的钱为" + cash);
}
}
线程不安全集合举例:ArrayList
import java.util.ArrayList;
import java.util.List;
//线程不安全集合举例:ArrayList
public class UnsafeSet {
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10000; i++) {
int finalI = i;
new Thread(()->{
list.add(finalI);
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
同步方法及同步块
同步方法:synchronized方法控制对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁。
同步块:synchronized(Obj){ },Obj成为“同步监视器”;
- Obj一般为共享资源对象;
- 同步方法中无需指定Obj,因为Obj就是this,这个对象本身;
上面三个线程不安全的例子可以通过同步方法或者同步块来解决;
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形;
某一同步块同时拥有两个以上对象的锁时,就可能发生死锁问题。
举例:
//多个人共用一套化妆品时可能出现死锁
public class DeadLock {
public static void main(String[] args) {
MakeUp m1 = new MakeUp(0,"女1");
MakeUp m2 = new MakeUp(1,"女2");
new Thread(m1,"1").start();
new Thread(m2,"2").start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class MakeUp implements Runnable {
//静态变量,保证只有一份,这样才会产生死锁
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String name;//名字
public MakeUp(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
makeUp();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void makeUp() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.name + "获得口红锁");
Thread.sleep(1000);
synchronized (mirror) {
System.out.println(this.name + "获得镜子锁");
}
}
} else {
synchronized (mirror) {
System.out.println(this.name + "获得镜子锁");
Thread.sleep(1000);
synchronized (lipstick) {
System.out.println(this.name + "获得口红锁");
}
}
}
}
}
Lock锁
从JDK5.0开始,Java提供了显式的同步锁java.util.concurrent.locks.Lock接口;
以Lock比较常用的实现类ReentrantLock举例:
import java.util.concurrent.locks.ReentrantLock;
public class TestLock implements Runnable {
int ticket = 1000;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock();
try {
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+ticket--);
}else
break;
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock, "张三").start();
new Thread(testLock, "李四").start();
new Thread(testLock, "王五").start();
}
}
本文详细介绍了线程同步的重要性,通过多人买票和取款的线程不安全案例展示了并发问题。同步方法如`synchronized`关键字用于解决这些问题,避免数据竞争。同时,死锁的概念和示例揭示了多线程间的资源争夺可能导致的僵局。最后,Java从JDK5.0引入的Lock接口,特别是ReentrantLock,提供了更灵活的锁机制,以提高并发程序的安全性和效率。
1064

被折叠的 条评论
为什么被折叠?



