Java基础:多线程

一:java中实现多线程的常用方式

            1、继承Thread类

public class Test1Thread {
	/*
	 * 
		- 继承Thread类实现多线程的步骤(java.lang.Thread)
		(1)声明类并继承Thread类
		(2)重写Thread类的run方法
		(3)创建定义的线程类的对象
		(4)通过调用start方法,启动线程。
		
	 */
	public static void main(String[] args) {
		test1();
	}

	private static void test1() {
		//3、创建定义的线程类对象
		Thread t1 = new WatchMovie();
		//4、通过调用start方法,启动线程。
		t1.start();
		//再启动发送弹幕的线程
		Thread t2 = new DanMu();
		//启动t2线程
		t2.start();
		//main方法也是个线程:main线程
		for(int i=0;i<=20;i++) {
			System.out.println("这是main线程:执行"+i+"次");
		}
	}
}
//继承Thread类实现多线程的步骤(java.lang.Thread)
//(1)声明类并继承Thread类
class WatchMovie extends Thread{

	//(2)重写Thread类的run方法:线程启动时会默认调用run方法。
	@Override
	public void run() {
		//循环播放视频20次
		for(int i=1;i<=20;i++) {
			System.out.println("播放视频"+i+"次");
			try {
				//每打印一次,让线程休眠1秒钟
				Thread.sleep(1000);//单位为毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//发送弹幕
//1、继承Thread类
class DanMu extends Thread{

	//2、重写run方法
	@Override
	public void run() {
		//循环发送20次弹幕
		for(int i=1;i<=20;i++) {
			System.out.println("发送弹幕"+i+"次");
			//休眠1秒钟
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

            2、实现Runnable接口

public class Test2Runnable {
	/*
	 * - 实现Runnable接口实现多线程的步骤(java.lang.Runnable)
		(1)声明类并实现Runnable接口
		(2)重写Runnable接口的run方法
		(3)创建实现Runnable接口类的实例对象
		(4)绑定线程(通过创建Thread类的实例对象实现线程绑定)
		(5)调用start方法,启动线程。
	 */
	public static void main(String[] args) {
		test1();
	}

	private static void test1() {
		//3)创建实现Runnable接口类的实例对象
		Runnable r1 = new PlayGame();
		//4、绑定线程(通过创建Thread类的实例对象实现线程绑定)
		Thread t1 = new Thread(r1);
		//(5)调用start方法,启动线程。
		t1.start();
		
		//创建对象
		Runnable r2 = new BGM();
		//绑定线程
		Thread t2 = new Thread(r2);
		//启动线程
		t2.start();
	}
}
//(1)声明类并实现Runnable接口
class PlayGame implements Runnable{

	//2)重写Runnable接口的run方法
	@Override
	public void run() {
		for(int i=1;i<=10;i++) {
			System.out.println("打游戏"+i+"分钟");
			//休眠1秒
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}
//播放游戏背景音乐
class BGM implements Runnable{
	//2)重写Runnable接口的run方法
	@Override
	public void run() {
		for(int i=1;i<=10;i++) {
			System.out.println("播放背景音乐"+i+"分钟");
			//休眠1秒
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

3匿名内部类创建线程

 // 实现runnable借口,创建多线程并启动
        new Thread(new Runnable() {
            @Override
             public void run() {
                 for (int x = 0; x < 100; x++) {
                     System.out.println(Thread.currentThread().getName() + "--"
                            + x);
                }
            }
        }) {
        }.start();

二:线程的优先级

       优先级指的是线程被CPU调度的概率,优先级越高,线程越容易被执行

       线程的优先级有十个级别:分别从1-10(10表示优先级最高,1表示优先级最低)

       线程默认优先级为5

       设置线程优先级的方法:

              setPriority(int newPriority);

       优先级的常量数字

               public static final int MAX_PRIORITY        表示10

               public static final int MIN_PRIORITY         表示1

               public static final int NORM_PRIORITY     表示5

三:守护线程

          守护线程一般是后台线程:执行的是支持(伴随)业务逻辑的线程;比如gc线程

          如果进程中有多个普通线程和守护线程同时存在,当所有普通线程执行完成以后,那么正在执行的守护线程也会随之结束

         设置守护线程:

                  setDaemon(true)

四:线程同步

        当多个线程访问同一对象时,可以利用线程同步使其他线程等待当前线程执行完成

        同步方法(synchronized):

                    获取的对象锁是调用该同步方法的对象(即锁住调用该同步方法的对象)

        同步块(synchronized):可以传入需要锁的对象obj(比较灵活)

        synchronized关键字会自动获取和释放对象锁

       对象锁:执行同步代码时会锁住对象每个对象都有一个对象锁,线程在执行synchronized修饰的代码时,会先自动获取对象

                    锁,阻止其他线程访问该对象,执行完synchronized(同步)的代码后,会自动释放对象锁。

public class Test4Sync {
	/*
	线程同步
	  现实生活中,我们经常会遇到“同一个资源,多个人都想使用”的问题。
	  这个的问题同样存在于Java当中,针对这一问题,Java引入了线程同步的概念:当多个线程访问同一对象时,可以利用线程同步使其他线程等待当前线程执行完成。
	  
	  例如:你和你老婆共用同一个银行账户,某一天你俩刚好都要取钱:
	  		现在账户里面只有1000块钱,你老婆需要取出600块买包,你需要取出600块钱洗脚。你俩心想钱是不是都够自己所需啊。
	  
	  线程同步:由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。
	  		    Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。
      			由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和 synchronized 块。
      	 - 同步方法(synchronized):获取的对象锁是调用该同步方法的对象(即锁住调用该同步方法的对象)。
      		- 语法:public [synchronized] void 方法名(){}
      	 - 同步块(synchronized):可以传入需要锁的对象obj(比较灵活)
      		- synchronized (被锁的对象obj){}
        - 对象锁:执行同步代码时会锁住对象
        	- 每个对象都有一个对象锁,线程在执行synchronized修饰的代码时,会先自动获取对象锁,阻止其他线程访问该对象;
        		执行完synchronized(同步)的代码后,会自动释放对象锁。
        	- synchronized关键字会自动获取和释放对象锁。
	 */
	public static void main(String[] args) {
		test1();
	}
	private static void test1() {
		//创建一个银行账户
		Account accout = new Account("你和你老婆的账户", 1000);
		//创建你的线程
		Runnable r1 = new GetMoney(accout,600);
		//绑定线程
		Thread t1 = new Thread(r1,"你");
		//创建你老婆的线程对象
		Runnable r2 = new GetMoney(accout,600);
		Thread t2 = new Thread(r2,"你老婆");
		t2.setPriority(8);
		//启动线程
		t1.start();
		t2.start();
	}
}
/*
 *   例如:你和你老婆共用同一个银行账户,某一天你俩刚好都要取钱:
	  		现在账户里面只有1000块钱,你老婆需要取出600块买包,你需要取出600块钱洗脚。你俩心想钱是不是都够自己所需啊。
*/
//定义银行账户
class Account{
	private String name;//账户名
	private int money;//账户的钱
	
	//把取钱的功能定义在账户里面,这样就可以获取账户的对象锁
	public synchronized void getMoney(int money) {//money:表示准备取的钱
		//取钱:先判断账户余额是否充足
		if(money>this.getMoney()) {
			//账户余额不足
			System.out.println("账户余额不足,请及时充值。。。。");
			return;//结束方法
		}
		//让线程休眠2秒钟
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//正常取钱
		//this.setMoney(this.getMoney()-money);
		this.money = this.money - money;
		//打印取钱成功的信息
		System.out.println("恭喜"+Thread.currentThread().getName()+"取钱成功,一共取了"+money+"元,账户余额围为:"+this.getMoney()+"元。");
	}
	public Account(String name, int money) {
		super();
		this.name = name;
		this.money = money;
	}
	public Account() {
		super();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getMoney() {
		return money;
	}
	public void setMoney(int money) {
		this.money = money;
	}
}
//取钱的任务
class GetMoney implements Runnable{
	//定义账户
	Account account;
	//取钱的数目
	int money;
	
	//重写run方法
	@Override
	public void run() {
		//打印谁要取钱
		System.out.println(Thread.currentThread().getName()+"准备去取钱。。。。。");
		System.out.println(Thread.currentThread().getName()+"准备取"+money+"元。。。");
		System.out.println(Thread.currentThread().getName()+"开始取钱。。。。");
		//开始取钱
		//this.getMoney();
		
		//调用账户取钱的功能
		//account.getMoney(600);//可以同步,但是有点麻烦
		//推荐使用 - 同步块(synchronized):可以传入需要锁的对象obj(比较灵活)
		synchronized (account) {
			//取钱:先判断账户余额是否充足
			if(money>account.getMoney()) {
				//账户余额不足
				System.out.println("账户余额不足,请及时充值。。。。");
				return;//结束方法
			}
			//让线程休眠2秒钟
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//正常取钱
			account.setMoney(account.getMoney()-money);
			//打印取钱成功的信息
		}
		System.out.println("恭喜"+Thread.currentThread().getName()+"取钱成功,一共取了"+money+"元,账户余额围为:"+account.getMoney()+"元。");
	}
	//定义取钱的功能:这样定义功能,会造成资源访问冲突的问题,造成账户余额异常,出现负数。 - 需要使用线程同步来解决
	/*public void getMoney() {
		//取钱:先判断账户余额是否充足
		if(money>account.getMoney()) {
			//账户余额不足
			System.out.println("账户余额不足,请及时充值。。。。");
			return;//结束方法
		}
		//让线程休眠2秒钟
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//正常取钱
		account.setMoney(account.getMoney()-money);
		//打印取钱成功的信息
		System.out.println("恭喜"+Thread.currentThread().getName()+"取钱成功,一共取了"+money+"元,账户余额围为:"+account.getMoney()+"元。");
	}*/
	//定义为同步方法:synchronized表示同步 - 也不能解决问题,获取的对象锁不对。
	/*public synchronized void getMoney() {//获取的对象锁:是调用该同步方法的对象的锁(也即是GetMoney的对象),不是account的对象。
		//取钱:先判断账户余额是否充足
		if(money>account.getMoney()) {
			//账户余额不足
			System.out.println("账户余额不足,请及时充值。。。。");
			return;//结束方法
		}
		//让线程休眠2秒钟
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//正常取钱
		account.setMoney(account.getMoney()-money);
		//打印取钱成功的信息
		System.out.println("恭喜"+Thread.currentThread().getName()+"取钱成功,一共取了"+money+"元,账户余额围为:"+account.getMoney()+"元。");
	}*/
	public GetMoney(Account account, int money) {
		super();
		this.account = account;
		this.money = money;
	}
	public GetMoney() {
		super();
	}
	
}

五:死锁

        线程死锁:两个线程同时等待对方释放资源,但是自己没有释放资源,这就会造成线程死锁

        线程死锁一般是由于逻辑问题造成的,通常都是由于同步的嵌套使用

        如何解决线程死锁的问题:

                 制定合理的业务逻辑:比如说规定抢占资源的顺序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值