| @Author:TTODS 使用多线程可以让我们的程序更加充分的利用CPU,提高程序的效率,但是同时也带来了一些问题。多线程在使用线程公共资源的时候往往会遇到问题。 运行结果 从上面的代码和运行结果我们可以看到,因为顾客几乎是同时到达商店的,所以他们到的时候商品的水量都是3,于是商店给每一位顾客都出售了一件商品。这显然不是我们想要的结果。然后我们给Store类的sell()方法加上synchronized关键字,局部修改如下: 运行结果: 可以看到在我们给 为了介绍线程同步的三个方法,我们先来看一下这样一个场景:有一个房间,房间里居住着四个人,四个人共用着房间里的一部电话。每个人都可以在任何时候使用电话,但是如果他们在同一时间都来使用这部电话又会发生什么呢? 运行结果 我们可以发现当线程s1使用了对象 运行结果 运行结果 运行结果 线程死锁,是指多个线程在使用公共资源时各占了资源的一部分,导致每个线程都等待其他线程释放资源才能完成工作,最终导致每个线程都无法完成工作,互相卡死。 运行结果(程序并没有结束而是一直卡住无法往下运行)
Java多线程入门(二)
线程同步
为什么要线程同步?
想象一下这样一个情景:一个商店中有三件商品,但是四个顾客同时来购买商品,他们都能买到商品吗?
我们用代码模拟一下:public class Test{ public static void main(String[] args) { //创建一个商店 Store store = new Store(); //创建四个共用一个商店的顾客线程 CustomerThread ct1 = new CustomerThread("顾客1",store); CustomerThread ct2 = new CustomerThread("顾客2",store); CustomerThread ct3 = new CustomerThread("顾客3",store); CustomerThread ct4 = new CustomerThread("顾客4",store); //启动四个线程 ct1.start(); ct2.start(); ct3.start(); ct4.start(); } } //商店类 class Store { //剩余商品数量 static int goodsNumble = 3; public Store(){ } //出售一件商品 void sell() { //先判断商品是否售完 if(goodsNumble<=0) System.out.println("商店:出售失败,商品卖完了"); else { System.out.println("此时还有"+goodsNumble+"件商品"); goodsNumble--; System.out.println("售出一件商品,商品数量减1"); } } } class CustomerThread extends Thread{ private Store store; public CustomerThread(String name,Store store) { // TODO 自动生成的构造函数存根 setName(name); this.store = store; } @Override public void run() { buy(); } //购买商品,调用store的sell方法 public void buy() { store.sell(); } }
synchronized void sell() { //先判断商品是否售完 if(goodsNumble<=0) System.out.println("商店:出售失败,商品卖完了"); else { System.out.println("此时还有"+goodsNumble+"件商品"); goodsNumble--; System.out.println("售出一件商品,商品数量减1"); } }
sell
方法使用了synchronized
关键字后Stroe
会按照一定的顺序来为顾客服务,这就是synchronized
方法的效果.当当前线程使用synchronized
方法时,会给当前对象(store)上锁,上锁后其他线程在使用相同对象的方法时进入阻塞,需等待此线程中的该方法结束,才能继续运行。线程同步的三个方法
我们用代码模拟一下:public class SynchronizedMethod { public static void main(String[] args) { Room r = new Room(); //创建并启动4个学生线程 StudentThread s1 =new StudentThread("s1",r); StudentThread s2 =new StudentThread("s2",r); StudentThread s3 =new StudentThread("s3",r); StudentThread s4 =new StudentThread("s4",r); s1.start(); s2.start(); s3.start(); s4.start(); } } class Room { public Phone phone; public Room() { // TODO 自动生成的构造函数存根 phone = new Phone(); } } class Phone{ static final int FREE = 1,BUSY = 0; //电话的状态 private int status=FREE; public Phone() { } //判断电话是否空闲 public boolean isFree() { return status==FREE; } //打电话,半秒后挂断,为了打印电话使用者的名字,我们传入一个参数 public void call(String user) { if(!isFree()) { System.out.println("有人正在使用电话哦"); return; } status = BUSY; System.out.println(user+"打出了电话..."); try { Thread.sleep(500); hangUp(user); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } //挂电话 public void hangUp(String user) { status = Phone.FREE; System.out.println(user+"挂断了电话..."); } } class StudentThread extends Thread{ private Room room; public StudentThread(String name,Room room) { setName(name); this.room = room; } @Override public void run() { this.room.phone.call(getName()); } }
phone
的call
方法的时候,其他三个线程仍然使用了call方法。这就是线程不同步的效果。接下来我们分别用三种方法实现线程同步。
synchronized
方法
将Phone
类的call
声明为sychronized
方法(在call
方法前面加上关键字即可),修改部分代码如下: public synchronized void call(String user) { if(!isFree()) { System.out.println("有人正在使用电话哦"); return; } status = BUSY; System.out.println(user+"打出了电话..."); try { Thread.sleep(500); hangUp(user); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } }
synchronized
块
在call
函数内添加synchronized
代码块,此方法与上一方法相比,优点在于你不需将整个方法都变成同步的,只需把关键代码块变为同步即可,修改部分代码如下:public void call(String user) { synchronized (this) { if(!isFree()) { System.out.println("有人正在使用电话哦"); return; } status = BUSY; System.out.println(user+"打出了电话..."); try { Thread.sleep(500); hangUp(user); } catch (InterruptedException e) { e.printStackTrace(); } } }
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Phone{ private Lock lock= new ReentrantLock(); static final int FREE = 1,BUSY = 0; //电话的状态 private int status=FREE; public Phone() { } //判断电话是否空闲 public boolean isFree() { return status==FREE; } //打电话,半秒后挂断,为了打印电话使用者的名字,我们传入一个参数 public void call(String user) { //上锁 lock.lock(); if(!isFree()) { System.out.println("有人正在使用电话哦"); return; } status = BUSY; System.out.println(user+"打出了电话..."); try { Thread.sleep(500); hangUp(user); } catch (InterruptedException e) { e.printStackTrace(); } //释放锁 lock.unlock(); } //挂电话 public void hangUp(String user) { status = Phone.FREE; System.out.println(user+"挂断了电话..."); } }
线程死锁
想象一下这样的情景:上学时,你和你的同桌共用一个笔记本和一支钢笔,只是你的老师讲到了一个要点,于是你顺势抢到了钢笔,而你的同桌拿到了笔记本,谁也不肯让步,于是谁也无法完成笔记。
我们用代码模拟一下:public class DeadLock { public static void main(String[] args) { Stationery stationery = new Stationery(); //p1 takeNotes时先获取并锁定本子,然后再试图获取笔 Person p1 = new Person("p1",stationery) { @Override public void takeNotes() { synchronized (stationery.notebook) { getNotebook(); synchronized (stationery.pen) { getPen(); } } } }; //p2 takeNotes时先获取并锁定笔,然后再试图获取本子 Person p2 = new Person("p2",stationery) { @Override public void takeNotes() { synchronized (stationery.pen) { getPen(); synchronized (stationery.notebook) { getNotebook(); } } } }; p1.start(); p2.start(); } } //文具类(共用资源) class Stationery{ //笔 public static Pen pen = new Pen(); //笔记本 public static Notebook notebook= new Notebook(); } class Pen{ } class Notebook{ } abstract class Person extends Thread{ Stationery stationery; public Person(String name,Stationery s) { // TODO 自动生成的构造函数存根 setName(name); stationery = s; } //获取笔 Pen getPen() { System.out.println(getName()+":我拿打笔了"); try { Thread.sleep(1); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } return stationery.pen; }; //获取本子 Notebook getNotebook() { System.out.println(getName()+":我拿打笔记本了"); try { Thread.sleep(1); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } return stationery.notebook; }; //抽象方法 public abstract void takeNotes(); @Override public void run() { takeNotes(); }; }
线程的生命周期
Thread t = new Thread()
,线程对象被创建,此时还没有使用start()方法,启动线程t.start()
后进入就绪状态,但具体什么时候运行取决于CPU的调度Thread.sleep()
或者进行I/O操作等不在占用CPU,则由运行态进入阻塞态,待事件结束再进入就绪态t.stop()
线程结束,进入终止态(消亡状态)
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算