线程同步
并发
同一对象被多个线程同时操作
线程同步:
处理多线程问题时,多线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕1,下一个线程在使用
三大安全案例
//不安全取钱
package test1;
//不安全取钱
public class Demo2 {
public static void main(String[] args) {
Account sang = new Account("sang", 1000);
Bank bank = new Bank(sang,500,"me");
Bank bank1 = new Bank(sang,900,"other");
bank.start();
bank1.start();
}
}
//账户
class Account {
private String name;//账户名
private int money;//账户余额
public Account() {
}
public Account(String name, int money) {
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setMoney(int money){
this.money = money;
}
public int getMoney() {
return money;
}
}
//银行模拟取款
class Bank extends Thread{
Account account;//
private int gotoOut;
public Bank() {
}
public Bank(Account account, int gotoOut,String name) {
super(name);
this.account = account;
this.gotoOut = gotoOut;
}
@Override
public void run() {
int remainder = account.getMoney() - this.gotoOut;
if (this.gotoOut>account.getMoney()){
System.out.println(Thread.currentThread().getName() + "钱不够,请重新输入取款金额!");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(remainder);
System.out.println(account.getName() + "Bank金额剩余:" + account.getMoney());
System.out.println(Thread.currentThread().getName() + "取款" + account.getMoney());
}
public int getGotoOut() {
return gotoOut;
}
public void setGotoOut(int gotoOut) {
this.gotoOut = gotoOut;
}
}
package test1;
//不安全买票,线程不安全有负数
public class Demo1 {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"我").start();
new Thread(buyTicket,"黄牛党").start();
new Thread(buyTicket,"别人").start();
}
}
class BuyTicket implements Runnable{
private int TicketNum = 20;//票数
boolean flag = true;
@Override
public void run() {
while (flag){//外部停止方式
try {
Thread.sleep(100);
buy();
if (TicketNum <= 1){
flag = false;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void buy(){
System.out.println(Thread.currentThread().getName()
+ "买了"+ this.TicketNum--);
}
}
package test1;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{list.add(Thread.currentThread().getName());}).start();
}
Thread.sleep(1000);
System.out.println(list.size());//7219
}
}
同步方法
同步块
package test1;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}}).start();
}
Thread.sleep(1000);
System.out.println(list.size());//7219
}
}
//synchronized默认锁定是this.
@Override
public void run() {
//锁的对象就是变化的量,需要增删改的对象
synchronized (account){
int remainder = account.getMoney() - this.gotoOut;
//判断钱够不够
if (this.gotoOut>account.getMoney()){
System.out.println(Thread.currentThread().getName() + "钱不够,请重新输入取款金额!");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(remainder);
System.out.println(account.getName() + "Bank金额剩余:" + account.getMoney());
System.out.println(Thread.currentThread().getName() + "取款" + account.getMoney());
}
}
//synchronized同步关键字,锁的是this,对象
private synchronized void buy(){
System.out.println(Thread.currentThread().getName()
+ "买了"+ this.TicketNum--);
}
疑问,juc安全类型的集合
package test1;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
//线程安全的集合类
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}//有点疑惑,如果不加sleep类,还是不到10000,添加sleep后
Thread.sleep(1000);//不到1000毫秒也不会到10000
System.out.println(list.size());
}
}
死锁
package test1;
//多线程互相需要对方锁内的资源,形成僵持,成为死锁
public class DeadLock {
public static void main(String[] args) {
new MakeEat(0,"sang").start();
new MakeEat(1,"zhang").start();
}
}
//曲奇饼
class Cookie{
}
//饼干,烤饼
class Biscuit{
}
class MakeEat extends Thread{
int choose;
String name;
static Cookie cookie = new Cookie();//静态变量,只有一个,如果不是静态
static Biscuit biscuit = new Biscuit();//这不会出现死锁现象
@Override
public void run() {
//吃饼干
try {
eat();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
MakeEat(int choose,String name){
this.choose = choose;
this.name = name;
}
private void eat() throws InterruptedException {
if (choose == 0){//先吃曲奇饼在吃饼干
synchronized (cookie){
System.out.println("吃曲奇饼" + this.name);
Thread.sleep(3000);//多一会,保证出现死锁
synchronized (biscuit){
System.out.println("吃饼干" + this.name);
Thread.sleep(1000);
}
}
//解决只需将synchronized放出来一个即可
}else {
synchronized (biscuit){
System.out.println("吃饼干" + this.name);
Thread.sleep(1000);
synchronized (cookie){
System.out.println("吃曲奇饼" + this.name);
Thread.sleep(1000);
}
}
}
}
}
解决
private void eat() throws InterruptedException {
if (choose == 0){//先吃曲奇饼在吃饼干
synchronized (cookie){
System.out.println("吃曲奇饼" + this.name);
Thread.sleep(3000);//多一会,保证出现死锁
}
synchronized (biscuit){
System.out.println("吃饼干" + this.name);
Thread.sleep(1000);
}
//解决只需将synchronized放出来一个即可
}else {
synchronized (biscuit){
System.out.println("吃饼干" + this.name);
Thread.sleep(1000);
}
synchronized (cookie){
System.out.println("吃曲奇饼" + this.name);
Thread.sleep(1000);
}
}
死锁避免方法
Lock(锁)
ReentrantLock类实现了Lock,它拥有了与synchronized相同的并发性和内存语义,再实现线程安全控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁。
package test1;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock1 testLock1 = new TestLock1();
new Thread(testLock1).start();
new Thread(testLock1).start();
new Thread(testLock1).start();
}
}
class TestLock1 implements Runnable{
private int testNum = 20;
private final ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
reentrantLock.lock();
try {
while(this.testNum > 0){
Thread.sleep(100);
System.out.println(this.testNum--);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}