方法一:同步代码块
Accoun
package com.bjsxt.synch1;
/**
* 银行账户类
*/
public class Account {
private int balance = 600;//余额
/**
* 取款
* @param money
*/
public void withDraw(int money){
this.balance = this.balance -money;
}
/**
* 查看余额
* @return
*/
public int getBalance(){
return balance;
}
}
取款线程
package com.bjsxt.synch1;
/**
* 取款的线程
*
* 线程同步方式1:同步代码块
*
* 总结1:认识同步监视器(锁子)
* synchronized(同步监视器){ }
* 1)必须是引用数据类型,不能是基本数据类型
* 2)在同步代码块中可以改变同步监视器对象的值,不能改变其引用
* 3)尽量不要String和包装类Integer做同步监视器.如果使用了,只要保证代码块中不对其进行任何操作也没有关系
* 4)一般使用共享资源做同步监视器即可
* 5)也可以创建一个专门的同步监视器,没有任何业务含义
* 6)建议使用final修饰同步监视器
*
* 总结2:同步代码块的执行过程
* 1)第一个线程来到同步代码块,发现同步监视器open状态,需要close,然后执行其中的代码
* 2)第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去了cpu,但是没有开锁open
* 3)第二个线程获取了cpu,来到了同步代码块,发现同步监视器close状态,无法执行其中的代码,第二个线程也进入阻塞状态
* 4)第一个线程再次获取CPU,接着执行后续的代码;同步代码块执行完毕,释放锁open
* 5)第二个线程也再次获取cpu,来到了同步代码块,发现同步监视器open状态,重复第一个线程的处理过程(加锁)
* 强调:同步代码块中能发生线程切换吗?能!!! 但是后续的被执行的线程也无法执行同步代码块(锁仍旧close)
* 总结3:线程同步 优点和缺点
* 优点:安全
* 缺点:效率低下 可能出现死锁
*
* 总结4:其他
* 1)多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块,其他线程无法访问其中的任何一个代码块
* 2)多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块, 但是没有锁住使用其他同步监视器的代码块,其他线程有机会访问其他同步监视器的代码块
*/
public class AccountRunnable implements Runnable{
private final Account account = new Account();
final Object obj = new Object();
//byte [] buf = new byte[1];
//final String str = "bjsxt";
//final Integer in = 1234;
//int n = 5;
/**
* 线程体:取款的步骤
*/
@Override
public void run() {
//此处省略200句
synchronized (account){
if(account.getBalance()>=400){//余额足够 临界代码
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//就取款
account.withDraw(400);
System.out.println(Thread.currentThread().getName()+"取款成功,当前余额:"+account.getBalance());
}else{//余额不足
//给出提示
System.out.println(Thread.currentThread().getName()+"取款失败,余额不足,当前余额:"+account.getBalance());
}
}
//此处省略100句
}
public void method1(){
synchronized (account){
}
}
public void method2(){
synchronized (obj){
}
}
public void method3(){
synchronized (obj){
}
}
}
main
package com.bjsxt.synch1;
public class TestAccount {
public static void main(String[] args) {
//创建两个线程,模拟两个用户
Runnable runnable = new AccountRunnable();
Thread thread1 = new Thread(runnable,"张三");
Thread thread2 = new Thread(runnable,"张三妻子");
//启动两个线程,模拟两个用户取款
thread1.start();
thread2.start();
}
}
方法二:同步方法
银行账户类
package com.bjsxt.synch2;
/**
* 银行账户类
*/
public class Account {
private int balance = 600;//余额
/**
* 取款
* @param money
*/
public void withDraw(int money){
this.balance = this.balance -money;
}
/**
* 查看余额
* @return
*/
public int getBalance(){
return balance;
}
}
取款的线程
package com.bjsxt.synch2;
/**
* 取款的线程
*方式2:同步方法
*
* 1.不要给run()加锁
* 2. 非静态同步方法的锁:this
* 静态同步方法的锁:类名.class
* 3.同步方法和同步代码块哪种效率高
* 同步代码块效率高
*
*/
public class AccountRunnable implements Runnable{
private Account account = new Account();
/**
* 线程体:取款的步骤
*/
@Override
public void run() {
//此处省略200句
//取款
withDraw();
//此处省略100句
}
public synchronized void withDraw(){ //非静态同步方法的锁是this
if(account.getBalance()>=400){ //余额足够 临界代码
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//就取款
account.withDraw(400);
System.out.println(Thread.currentThread().getName()+"取款成功,当前余额:"+account.getBalance());
}else{//余额不足
//给出提示
System.out.println(Thread.currentThread().getName()+"取款失败,余额不足,当前余额:"+account.getBalance());
}
}
public synchronized void method1(){ //this
}
public synchronized void method2(){ //this
// Vector v;
}
public synchronized static void method3(){ //静态的同步方法的锁是类名.class 类对象 AccountRunnable.class
//Class clazz = AccountRunnable.class;
}
public synchronized static void method4(){
}
}
main
package com.bjsxt.synch2;
public class TestAccount {
public static void main(String[] args) {
//创建两个线程,模拟两个用户
Runnable runnable = new AccountRunnable();
Thread thread1 = new Thread(runnable,"张三");
Thread thread2 = new Thread(runnable,"张三妻子");
//启动两个线程,模拟两个用户取款
thread1.start();
thread2.start();
}
}
方法三:锁
银行账户类
package com.bjsxt.synch3;
/**
* 银行账户类
*/
public class Account {
private int balance = 600;//余额
/**
* 取款
* @param money
*/
public void withDraw(int money){
this.balance = this.balance -money;
}
/**
* 查看余额
* @return
*/
public int getBalance(){
return balance;
}
}
取款的线程
package com.bjsxt.synch3;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 取款的线程
*/
public class AccountRunnable implements Runnable{
private Account account = new Account();
//购买一把锁
private Lock lock = new ReentrantLock();//Re-entrant-Lock //entrance 可重入锁
/**
* 线程体:取款的步骤
*/
@Override
public void run() {
//此处省略200句
//上锁
lock.lock();
try{
if(account.getBalance()>=400){//余额足够 临界代码
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//就取款
account.withDraw(400);
System.out.println(Thread.currentThread().getName()+"取款成功,当前余额:"+account.getBalance());
}else{//余额不足
//给出提示
System.out.println(Thread.currentThread().getName()+"取款失败,余额不足,当前余额:"+account.getBalance());
}
method1();
}finally{
//解锁
lock.unlock();
}
//此处省略100句
}
public void method1(){
lock.lock();
try{
method2();
}finally{
lock.unlock();
}
}
public void method2(){
lock.lock();
try{
}finally{
lock.unlock();
}
}
}
main
package com.bjsxt.synch3;
public class TestAccount {
public static void main(String[] args) {
//创建两个线程,模拟两个用户
Runnable runnable = new AccountRunnable();
Thread thread1 = new Thread(runnable,"张三");
Thread thread2 = new Thread(runnable,"张三妻子");
//启动两个线程,模拟两个用户取款
thread1.start();
thread2.start();
}
}