javaSE---多线程03

30 篇文章 0 订阅
4 篇文章 0 订阅

多线程3

通过继承Thread类所创建的线程不能实现资源共享功能,
例如:

package com.wangxing.thread.test1;

import java.io.IOException;
import java.nio.CharBuffer;

public class MyThread  implements Runnable {
	//定义票数
	private int piao=5;

	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		// TODO Auto-generated method stub
		 boolean flag=true;
		 while(flag){
			 if(piao>0){
				 //线程休眠1秒
				 try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				 piao=piao-1;
				 System.out.println(name+"卖出1张票,还剩"+piao+"张");
			 }else{
				 flag=false;
			 }
	}
	}
}

测试类:

package com.wangxing.thread.test1;

public class TestMain {

	public static void main(String[] args) {
		//目标对象
		MyThread myth=new MyThread();
		
		//线程对象
		Thread tth1=new Thread(myth);
		Thread tth2=new Thread(myth);
		Thread tth3=new Thread(myth);
		//修改线程名称
		tth1.setName("窗口一");
		tth2.setName("窗口二");
		tth3.setName("窗口三");
		//运行线程
		tth1.start();
		tth2.start();
		tth3.start();
	}

}

在这里插入图片描述
通过上面的实现Runnable接口的买票程序可以实现资源共享,但是卖出会卖出剩下负数的情况。
分析:
当窗口3开始卖最后一张票的时候,窗口3判断还有一张票,这时窗口3开始收钱打票,当窗口3开始收钱打票的时候,线程就切换给了窗口1,由于窗口3还有来得及对票数减1,因此窗口1判断还有一张票,这时窗口1开始收钱打票,当窗口1开始收钱打票的时候,线程就切换给了窗口2,由于窗口1还有来得及对票数减1,因此窗口2判断还有一张票,这时窗口2开始收钱打票,线程切换给了窗口3,所以窗口3输出“窗口3卖出1张票,还剩0张”,输出结束以后线程就切换给窗口1,由于窗口3已经对票数减1,所以窗口1输出剩余票数的时候在窗口3减1以后的基础上再一次减1,就得到剩余-1张票,所以窗口1输出“窗口1卖出1张票,还剩-1张”,输出结束以后线程就切换给窗口2,由于窗口1已经对票数减1,所以窗口2输出剩余票数的时候在窗口1减1以后的基础上再一次减1,就得到剩余-2张票,所以窗口2输出“窗口2卖出1张票,还剩-2张”.
经过上面运行程序的分析,我得到的结果是:
当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。
为了解决这种数据不一致的错误情况,我们才学习线程同步。

10.5.为什么需要线程同步/线程安全?
因为当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。为了解决这种数据不一致的错误情况,我们才学习线程同步。

10.6.什么是线程同步/线程安全?
线程同步:当多条线程,同时访问同一个资源的时候,每一次只能由多条线程中的其中一条访问公共资源,当这一条线程访问公共资源的时候,其他的线程都处于等待状态,不能访问公共资源,当这一条线程访问完了公共资源以后,其他线程中的一条线程才能访问资源,剩下的线程继续等待,等待当前线程访问结束,实现这个过程就是线程同步也叫线程安全。

10.7.线程同步/线程安全的实现方式有几种,分别是什么,有什么区别?
有2中方式可以实现线程同步/线程安全
1. Synchronized关键字 【同步代码块/同步方法】
1.1同步代码块
格式:
synchronized(同步对象){

}
例如:

package com.wangxing.thread.test1;
/**
 * 基于Synchronized关键字代码块
 * synchronized(同步对象)
 * @author Administrator
 *
 */
public class SynchronizedDemo1 implements Runnable {
    //多线程同步代码块
	//票数
	private int piao=5;
	@Override
	public void run() {
		//得到当前线程name
		String name=Thread.currentThread().getName();
		boolean flag=true;
		
		while (flag) {
			//同步代码块
			//被访问资源所在类对象
			synchronized (this) {
		     if(piao>0){	
			     try {
			         Thread.sleep(1000);
		          } catch (InterruptedException e) {  
			      e.printStackTrace();
		        }  
				piao=piao-1;
				System.out.println(name+"卖出一张票还剩"+piao+"张");
			}else{
				flag=false;
			}
		  }
		}
	}	
}

测试类:

package com.wangxing.thread.test1;

public class TestMain {

	public static void main(String[] args) {
		//目标对象
	    SynchronizedDemo1 myth=new SynchronizedDemo1();
		
		//线程对象
		Thread tth1=new Thread(myth);
		Thread tth2=new Thread(myth);
		Thread tth3=new Thread(myth);
		//修改线程名称
		tth1.setName("窗口一");
		tth2.setName("窗口二");
		tth3.setName("窗口三");
		//运行线程
		tth1.start();
		tth2.start();
		tth3.start();
	}

}

在这里插入图片描述
同步代码块虽然可以实现买票的效果,但是它在使用的时候,需要设置一个同步对象,由于我们很多时候都不知道这个同步对象应该是谁,容易写错,造成死锁的情况。正是应为这个缺点,我们很少使用同步代码块来实现线程同步。

1.2同步方法
同步方法也是方法,所以它一定是符合方法的定义格式的。
方法的定义格式:
访问限制修饰符 方法返回值类型 方法名称(){

}
同步方法的定义格式:
访问限制修饰符 synchronized 方法返回值类型 方法名称(){

}

例如:

package com.wangxing.thread.test1;
/**
 * Synchronized关键字的同步方法
 * @author Administrator
 *
 */
public class SynchronizedMethodDemo1 implements Runnable {
	//票
	private int piao=5;
	//控制方法退出的boolean
	boolean flag=true;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		//得到线程名称
		String name=Thread.currentThread().getName();
		while(flag){
			
			sellpiao(name);
		}
	}
	private synchronized void sellpiao(String name){
	     if(piao>0){	
		     try {
		         Thread.sleep(1000);
	          } catch (InterruptedException e) {  
		      e.printStackTrace();
	        }  
			piao=piao-1;
			System.out.println(name+"卖出一张票还剩"+piao+"张");
		}else{
			flag=false;
		}
	}
}

测试类:

package com.wangxing.thread.test1;

public class TestMain {

	public static void main(String[] args) {
		//目标对象
        SynchronizedMethodDemo1 myth=new SynchronizedMethodDemo1();
		
		//线程对象
		Thread tth1=new Thread(myth);
		Thread tth2=new Thread(myth);
		Thread tth3=new Thread(myth);
		//修改线程名称
		tth1.setName("窗口一");
		tth2.setName("窗口二");
		tth3.setName("窗口三");
		//运行线程
		tth1.start();
		tth2.start();
		tth3.start();
	}

}

在这里插入图片描述
2. 通过Lock接口
public interface Lock
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作
常用的接口方法

voidlock()获得锁
voidunlock()释放锁

由于上面的锁方法是Lock接口,我们要使用就得先创建出Lock接口对象,由于Lock是个接口不能new ,我们就得使用它的子类来创建对象。
Lock接口得子类ReentrantLock
ReentrantLock() 创建一个 ReentrantLock的实例。
例如:

package com.wangxing.thread.test1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo implements Runnable {
	//定义票数
	private int piao=5;
	//得到lock锁对象
	private Lock  lock=new ReentrantLock();
	//或者
	//private   ReentrantLock  reentrantLock=new ReentrantLock();

	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		// TODO Auto-generated method stub
		 boolean flag=true;
		 while(flag){
			 //void lock()获得锁
			 lock.lock();
			 if(piao>0){
				 //线程休眠1秒
				 try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				 piao=piao-1;
				 System.out.println(name+"卖出1张票,还剩"+piao+"张");
			 }else{
				 flag=false;
			 }
			 //void unlock()释放锁
			 lock.unlock();
	}
	}
}

测试类:

package com.wangxing.thread.test1;

public class TestMain {

	public static void main(String[] args) {
		//目标对象
		LockDemo myth=new LockDemo();
		
		//线程对象
		Thread tth1=new Thread(myth);
		Thread tth2=new Thread(myth);
		Thread tth3=new Thread(myth);
		//修改线程名称
		tth1.setName("窗口一");
		tth2.setName("窗口二");
		tth3.setName("窗口三");
		//运行线程
		tth1.start();
		tth2.start();
		tth3.start();
	}

}

synchronizedLock
关键字接口
自动锁定资源手动锁定资源
不灵活灵活
异常时会自动释放锁不会自动释放锁,所以需要在finally中实现释放锁
不能中断锁,必须等待线程执行完成释放锁。可以中断锁
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值