一、线程创建和互斥
创建多个线程,对银行账户执行存款和取款操作
1、继承Runnable接口,创建线程类MyBank
public class MyBank implements Runnable {
private final Bank bank;
private final Long withdrawMoney;
private final Long depositMoney;
public MyBank(Bank b,Long m1,Long m2){
this.bank=b;
this.withdrawMoney=m1;
this.depositMoney=m2;
}
public void run(){
System.out.println("thread is running.");
while(true){
savingMoney(bank,depositMoney);
withdrawMoney(bank,withdrawMoney);
}
}
public void savingMoney(Bank bank,Long m){
bank.deposit(m);
System.out.println(bank.getAccount()+" deposit "+m+" ¥, current balance="+bank.getMoney());
}
public void withdrawMoney(Bank bank,Long m){
if(bank.withdraw(m)){
System.out.println(bank.getAccount()+" withdraw "+m+" ¥, current balance="+bank.getMoney());
}else{
System.out.println(bank.getAccount()+" withdraw failed,not enough money. current balance="+bank.getMoney());
}
}
}
2、创建银行类Bank, 使用synchronized关键字实现线程互斥
public class Bank {
private Long money;
private String account;
public Bank(String account,Long money){
this.money=money;
this.account=account;
}
public synchronized void deposit(Long save){
money+=save;
}
public synchronized boolean withdraw(Long m){
if(money>=m){
money-=m;
return true;
}else{
return false;
}
}
public String getAccount(){
return account;
}
public Long getMoney(){
return money;
}
}
3、在Main类中创建多个线程,多银行实现多线程读写
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Main {
public static void main(String[] args) {
//通过Thread创建线程
// PrintThread thread1=new PrintThread("thread 1 running");
// PrintThread thread2=new PrintThread("thread 2 running");
// thread1.start();
// thread2.start();
//线程暂停
/* for(int i=0;i<10;i++){
System.out.println("Good");
try{
Thread.sleep((1000));
}catch (InterruptedException e){
e.printStackTrace();
}
}*/
//使用ThreadFactory创建线程
ThreadFactory factory= Executors.defaultThreadFactory();
Bank mybank=new Bank("ALEXon",1000L);
MyBank t1=new MyBank(mybank,10L,5L);
MyBank t2=new MyBank(mybank,5L,10L);
factory.newThread(t1).start();
factory.newThread(t2).start();
}
}
}
二、Single Thread Execution模式
该模式下,同一时间内只能允许一个线程执行处理,就像独木桥同一时间只允许一个人通行
SharedResource共享资源
SharedResource指可被多个线程访问的类,包含的方法分为两类:safeMethod和unsafeMethod
- safeMethod:多个线程同时调用也不会发生问题的方法
- unsafeMethod:多个线程同时调用会发生问题的方法,必须加以保护的方法;
临界区
只允许单个线程执行的程序范围称为临界区。
死锁
死锁是指两个线程分别持有着锁,相互等待着对方释放锁的现象;发生死锁的线程都无法继续执行,程序失去了生存性。
发生死锁的条件:
- 存在多个SharedResource角色
- 线程在持有着某个SharedResource角色的锁同时,还想获取其他SharedResource角色的锁
- 获取SharedResource角色的锁的顺序不固定(SharedResource角色是对称的)
原子操作
不可分割的操作
synchronized互斥关键字
-
synchronized可以保证共享资源同一时间只能被一个线程使用;
-
但是使用了synchronized的方法性能会比没有使用的差,因为要处理线程冲突,挂锁和解锁过程;
-
synchronized方法和synchronized代码块无论是执行return还是抛出异常,都一定能够释放锁。
synchronized实例
三个人不断的通过一个Gate门
Gate类
public class Gate {
private int counter=0;
private String name="nobaody";
private String address="Nowhere";
//synchronized关键字确保同一时间只有一个线程执行该方法
public synchronized void pass(String name,String address){
this.counter++;
this.name=name;
this.address=address;
check();
}
//synchronized关键字确保同一时间只有一个线程执行该方法
public synchronized String toString(){
return "No."+counter+": "+name+", "+address;
}
private void check(){
if(name.charAt(0)!=address.charAt(0)){
System.out.println("-------------Broken------------:"+toString());
}
}
}
UserThread类:
public class UserThread extends Thread{
private final Gate gate;
private final String myname;
private final String myaddress;
public UserThread(Gate gate,String myname,String address){
this.gate=gate;
this.myname=myname;
this.myaddress=address;
}
public void run(){
System.out.println(myname+" BEGIN.....");
while(true){
gate.pass(myname,myaddress);
}
}
}
Main类:
public class Main {
/*Single Thread Execution模式*/
System.out.println("Test Gate, hit CTRL+C to exit.");
Gate gate= new Gate();
new UserThread(gate,"Alex","Aubei").start();
new UserThread(gate,"Bob","Biangxi").start();
new UserThread(gate,"Jackson","Jeijing").start();
}
}
计数信号量
假设能够使用的资源个数是N个,而需要这些资源的线程个数大于N,就会导致资源竞争,这是需要使用计数信号量Semaphore类。
Semaphore中的acquire方法用于确保存在可用的资源,当存在可用资源时,线程会立即从acquire方法中返回,同时信号量内部的资源个数减1;如果没有可用资源时,线程则阻塞在acquire方法内,直至出现可用资源;
Semaphore中的release方法用于释放资源,释放后,信号量内部的资源个数加1;
acquire和release必须成对使用;
计数信号量实例
1、Log类
public class Log {
public static void println(String s){
System.out.println(Thread.currentThread().getName()+": "+s);
}
}
2、有限的资源类
import java.util.Random;
import java.util.concurrent.Semaphore;
/**
* 有限的资源类
*/
public class BoundedResource {
private final Semaphore semaphore;
private final int permits;//允许的资源个数
private final static Random random=new Random(314159);
public BoundedResource(int permits){
this.permits=permits;
this.semaphore=new Semaphore(permits);
}
public void use() throws InterruptedException{
// 获取资源
semaphore.acquire();
try{
// 获取资源后执行操作
doUse();
}catch (InterruptedException e){
e.printStackTrace();
} finally {
// 释放资源
semaphore.release();
}
}
protected void doUse() throws InterruptedException{
Log.println("BEGIN: used="+(permits-semaphore.availablePermits()));
Thread.sleep(random.nextInt(500));
Log.println("END used="+(permits-semaphore.availablePermits()));
}
}
3、线程创建类
import java.util.Random;
public class SemaphoreUserThread extends Thread {
private final static Random random=new Random(265353);
private final BoundedResource resource;
public SemaphoreUserThread(BoundedResource resource){
this.resource=resource;
}
public void run(){
try{
while(true){
resource.use();
Thread.sleep(random.nextInt(3000));
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
4、Main类
public class Main {
public static void main(String[] args) {
/*Semaphore计数信号量*/
// 创建资源,只允许4个线程使用
BoundedResource resource=new BoundedResource(4);
// 创建10个线程
for(int i=0;i<10;i++){
new SemaphoreUserThread(resource).start();
}
}
}