萌新学Java之渐入佳境二----线程应用

多个线程访问共享资源

解决步骤(以售票为例)

  • 1.尝试写代码
  • 2.发现问题
  •  票会被重复出售 有票被跳过了
    
  • 3.分析问题 如何出现的
  •  三个线程同时执行那个run方法 CPU执行资源随机分配
    
  •  线程在执行方法过程中 随时可以进入受阻塞状态
    
  •  所以可以使用 假设线程停止的位置 来分析问题(找极限位置)
    
  • 4.思考解决方法
  •  一个线程执行完买票操作 另一个线程才能买票
    
  •  这样来保证 共享数据的安全
    
  • 5.尝试解决
  • 同步代码块(同步锁)
  • 写法:
  • synchronized(锁){
  •  上锁的代码
    
  • }
  • 当代码进入同步代码块 会把锁拿走 执行代码块中的代码
  • 当代码执行完毕后 会把锁还回去
  • 如果线程遇到同步代码块 发现没有锁 将进入等待(有锁才能进去)
  • 锁:保证所有线程使用的都是同一把锁
  • 锁可以是任意一个对象

例(同步代码块):

//利用接口方法 来保证 访问的共享资源
class Tickets implements Runnable{
//票总数
private int tickets = 100;

//声明锁对象(保证唯一)
private final Object obj = new Object();

//买票
@Override
public void run() {
	// TODO Auto-generated method stub
	
	
		//保证票都能卖出
	while (true) {
		
		synchronized (obj) {
			
			//休眠放大问题
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//判断票
			if (tickets > 0) {
				//可以卖
				System.out.println(Thread.currentThread().getName() + "----" + tickets);
				//卖票
				tickets--;
			}else {
				//卖完了
				break;
			}
		}
		//让线程让出CPU的执行资源(可能让出 增加几率)
		Thread.yield();
	}
}}

public static void main(String[] args) {
//利用接口的实现类 创建三个线程出来
Tickets tickets = new Tickets();
//创建三个线程
Thread t1 = new Thread(tickets);
Thread t2 = new Thread(tickets);
Thread t3 = new Thread(tickets);
//开启线程
t1.start();
t2.start();
t3.start();
}

例(synchronized):

//利用接口方法 来保证 访问的共享资源
class Tickets1 implements Runnable{
//票总数
private int tickets = 100;

//声明锁对象(保证唯一)
private final Object obj = new Object();

//买票
@Override
public void run() {
	// TODO Auto-generated method stub
	
	
		//保证票都能卖出
	while (true) {
		if (sellTickets()) {
			break;
		}
		//让线程让出CPU的执行资源(可能让出 增加几率)
		Thread.yield();
	}
}
//封装一个方法
//使用同步方法		
//写法:使用synchronized关键词修饰方法
//原理跟同步代码块一样 使用的对象锁是 this
//同步方法 可以是静态方法 但同时要求成员变量也是静态的
//但是静态的同步方法不能用this  使用类锁 类.class
public synchronized boolean sellTickets() {
	if (tickets > 0) {
		System.out.println(Thread.currentThread().getName() + "----" + tickets);
		tickets--;
		return false;
	}else {
		//卖完了
		return true;
	}
}

}

jdk1.5 Lock 接口

  • lock();加锁方法

  • unlock();释放锁方法

  • 保证出现异常时 也能把锁关闭

  • 写法:

  • lock();

  • try{

  • 加锁的代码

  • }finally{

  • 释放锁

  • unlock();

  • }
    例:
    //利用接口方法 来保证 访问的共享资源
    class Tickets3 implements Runnable{
    //票总数
    private int tickets = 100;

    //声明锁对象(保证唯一)
    private final Object obj = new Object();

     //声明lock锁
     //参数:true 可以尽量让线程公平进入锁(不一定)	
     private final ReentrantLock lock = new ReentrantLock(true);
    

    //买票
    @Override
    public void run() {
    // TODO Auto-generated method stub

     	//保证票都能卖出
     while (true) {
     	//使用lock锁
     	lock.lock();
     	try {
     		if (tickets > 0) {
     			System.out.println(Thread.currentThread().getName() + "----" + tickets);
     			tickets--;
     		}else {
     			break;
     		}
     	} finally {
     		// TODO: handle finally clause
     		//释放锁
     		lock.unlock();
     	}
     	
     	//让线程让出CPU的执行资源(可能让出 增加几率)
     	Thread.yield();
     }
    

    }
    }

死锁

前提:

  • 1.至少两个线程

  • 2.锁的嵌套(同步代码块的的嵌套)

  • 3.两把锁

     线程1和线程2同时访问有嵌套的同步代码块程序
     并且有两个锁 A和B
     线程1拿到了A  向进入下一个代码块需要B锁
     线程2拿到了B  向进入下一个代码块需要A锁
     这时谁也进不去 线程进入相互等待的状态 导致程序卡住
    

例:

//声明锁
class LockA{
//私有化构造方法
private LockA() {
}
//创建锁对象(声明常量)
public static final LockA A = new LockA();
}
class LockB{
//私有化构造方法
private LockB() {
}
//创建锁对象(声明常量)
public static final LockB B = new LockB();
}
//线程
class DeadLockRunnable implements Runnable{
//利用标记来控制 先A->B 或 先B->A
boolean isTrue = true;
@Override
public void run() {
// TODO Auto-generated method stub
//利用死循环 增加死锁几率
while (true) {
//不断地让两个线程先进A锁再进B锁下次从B锁进A锁
if (isTrue) {
//先进A锁再进B锁
synchronized (LockA.A) {
System.out.println(“if A锁”);
synchronized (LockB.B) {
System.out.println(“if B锁”);
}
}
}else {
//下一次从B锁进A锁
synchronized (LockB.B) {
System.out.println(“else B锁”);
synchronized (LockA.A) {
System.out.println(“else A锁”);
}
}
}
//改变标记
isTrue = !isTrue;
}
}
}

public static void main(String[] args) {
DeadLockRunnable runnable = new DeadLockRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
}

线程停止

调用stop()方法 但已经过时了 不推荐使用

interrupt() 中断线程? 不能中断线程

  • 中断状态将被设置
  • 1.可以改变中断状态 就是一个布尔值 初值false --> true
  • 2.当你这个线程中 使用sleep wait join方法时
  •  会抛出个异常InterruptException
    
  •  中断状态将被清除 这是interrupt()的值还是false
    

正确方式:使用标记停止线程

例:

class Interrupt implements Runnable{
//声明标记 控制线程的停止
public boolean isTrue = false;
@Override
public void run() {
while (!isTrue) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(11);
//InterruptedException 中断异常
// Thread.interrupted();
System.out.println(22);
}
}
}

public static void main(String[] args) throws InterruptedException {
Interrupt interrupt = new Interrupt();
Thread t1 = new Thread(interrupt);
t1.start();
//休眠几秒 给子线程运行的时间
Thread.sleep(3000);
//中断线程
// t1.interrupt();
interrupt.isTrue = true;
System.out.println(“线程中断”);
//让主线程运行一秒
//中断状态被清除指的是 从休眠状态—>运行状态(或者受阻塞)
Thread.sleep(1000);
System.out.println(“主线程结束”);
}

wait方法

class WaitRunnable implements Runnable{
//声明标记 停止线程
public boolean isTrue = false;
//声明锁
private final Object obj = new Object();
@Override
public synchronized void run() {
// TODO Auto-generated method stub
while (!isTrue) {
System.out.println(Thread.currentThread().getName());
try {
//使用线程等待
//wait方法需要使用锁对象来调用
//将线程从等待状态—>运行状态(受阻塞状态)
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}}

public static void main(String[] args) throws InterruptedException {
WaitRunnable runnable = new WaitRunnable();
Thread t1 = new Thread(runnable);
t1.start();

	Thread.sleep(3000);
	for (int i = 0; i < 10; i++) {
		if (i == 5) {
			//interrupt方法
			//t1.interrupt();
			//让线程停止
			//runnable.isTrue = true;
		}
	}
	Thread.sleep(1000);
	System.out.println("主线程停止");
}

如何立即接收线程状态

当你从子线程中 修改状态时 主线程不能立即接收到这个状态的改变
使用关键词 volatile 来标识你改变的状态的变量

效果是:可以让主线程立即接收到改变的值

class ChangeRunnable implements Runnable{
//当你从子线程中 修改状态时 主线程不能立即接收到这个状态的改变
//使用关键词 volatile 来标识你改变的状态的变量
//效果是:可以让主线程立即接收到改变的值
public volatile boolean isTrue = false;
//记录循环次数
private int num = 0;
@Override
public void run() {
// TODO Auto-generated method stub
while (!isTrue) {
num++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (num == 5) {
//修改状态
isTrue = true;
}
System.out.println(Thread.currentThread().getName() + “–” + num);
}
}
}

public static void main(String[] args) {
ChangeRunnable runnable = new ChangeRunnable();
Thread thread = new Thread(runnable);
thread.start();

	//利用线程中标记 卡住主线程
	while (!runnable.isTrue) {
		
	}
	System.out.println("主线程结束");
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值