后台线程
在Java程序中,只要前台有一个线程在运行,则整个java进程都不会消失,所以此时可以设置一个后台线程,
这样即使Java进程结束了,此后台线程依然会继续执行。要想实现这样的操作,直接使用setDaemon()方法即可。
礼让yield
public class ThreadYield implements Runnable { @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i==3){ Thread.currentThread().yield();//当前线程礼让 } } } public static void main(String[] args) { ThreadYield ty =new ThreadYield(); Thread t1 = new Thread(ty, "A"); t1.start(); Thread t2 = new Thread(ty, "B"); t2.start(); } }public class ThreadYield implements Runnable { @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); if(i==3){ Thread.currentThread().yield();//当前线程礼让 } } } public static void main(String[] args) { ThreadYield ty =new ThreadYield(); Thread t1 = new Thread(ty, "A"); t1.start(); Thread t2 = new Thread(ty, "B"); t2.start(); } }
线程合并join
class ThreadJoin1 extends Thread{ @Override public void run() { for (int i = 1; i <=10; i++) { //this.sleep(200); System.out.println(this.getName()+":"+i); } } } class ThreadJoin2 extends Thread{ @Override public void run() { try { for (int i = 1; i <=10; i++) { System.out.println(this.getName()+":"+i); if(i==3){ //TheadJoin1执行完 ThreadJoin1 tt = new ThreadJoin1(); tt.setName("one"); // tt.join();//加入 tt.start(); tt.join();//加入 } } } catch (InterruptedException e) { e.printStackTrace(); } } } public class str { public static void main(String[] args) { ThreadJoin2 t = new ThreadJoin2(); t.setName("two"); t.start(); } }
这里需要注意:join需要放在start后面
线程同步:
买票的例子:
有10张票,有2个售票窗口同时卖票,要求不能一个窗口一次都卖完,所有的票总和最终是10张;
共享数据,没有被所有线程共享
public class Ticket extends Thread { int num = 10;// 票数10张 @Override public void run() { while (num > 0) { num--;// 买了一张 System.out.println(this.getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } System.out.println("售罄!!!"); } } public static void main(String[] args) { Ticket t1 = new Ticket(); t1.setName("窗口1"); t1.start(); Ticket t2 = new Ticket(); t2.setName("窗口2"); t2.start(); } 窗口1,卖了第:1,还剩:9张票! 窗口2,卖了第:1,还剩:9张票! 窗口1,卖了第:2,还剩:8张票! 窗口2,卖了第:2,还剩:8张票! 窗口1,卖了第:3,还剩:7张票! 窗口2,卖了第:3,还剩:7张票! 窗口2,卖了第:4,还剩:6张票! 窗口2,卖了第:5,还剩:5张票! 窗口2,卖了第:6,还剩:4张票! 窗口2,卖了第:7,还剩:3张票! 窗口1,卖了第:4,还剩:6张票! 窗口1,卖了第:5,还剩:5张票! 窗口1,卖了第:6,还剩:4张票! 窗口1,卖了第:7,还剩:3张票! 窗口1,卖了第:8,还剩:2张票! 窗口1,卖了第:9,还剩:1张票! 窗口1,卖了第:10,还剩:0张票! 售罄!!! 窗口2,卖了第:8,还剩:2张票! 窗口2,卖了第:9,还剩:1张票! 窗口2,卖了第:10,还剩:0张票! 售罄!!! 一共买了20张票
把共享资源,写成静态成员,实现数据的共享
public class Ticket extends Thread { static int num = 10;// 票数10张,共享资源同步 @Override public void run() { while (num > 0) { num--;// 买了一张 System.out.println(this.getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } System.out.println("售罄!!!"); } } public static void main(String[] args) { Ticket t1 = new Ticket(); t1.setName("窗口1"); t1.start(); Ticket t2 = new Ticket(); t2.setName("窗口2"); t2.start(); } 窗口1,卖了第:1,还剩:9张票! 窗口2,卖了第:1,还剩:9张票! 窗口1,卖了第:2,还剩:8张票! 窗口2,卖了第:3,还剩:7张票! 窗口1,卖了第:4,还剩:6张票! 窗口2,卖了第:4,还剩:6张票! 窗口2,卖了第:6,还剩:4张票! 窗口1,卖了第:6,还剩:4张票! 窗口2,卖了第:7,还剩:3张票! 窗口1,卖了第:7,还剩:3张票! 窗口2,卖了第:8,还剩:2张票! 窗口1,卖了第:8,还剩:2张票! 窗口1,卖了第:9,还剩:1张票! 窗口2,卖了第:9,还剩:1张票! 窗口2,卖了第:10,还剩:-1张票! 售罄!!! 窗口1,卖了第:11,还剩:-1张票! 售罄!!! 虽然共享数据已经是共享的,但结果还是乱的;
共享数据同步操作
如果想解决这样的问题,就必须使用同步,所谓的同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
同步代码块和同步方法两种方式完成。
同步代码块
在代码块上加上“synchronized”关键字的话,则此代码块就称为同步代码块。 同步代码块格式: synchronized(同步对象){ 需要同步的代码 ; } 只有唯一的对象才能作为对象锁使用!!! 对象锁 this new Object new Tikcet() Class类型 Person.class Student.class Animal.class this代表的是当前类对象,继承Thread类,开启线程会实例化多次,每个线程对象都有自己的this;所以不能用this作为对象锁!!! 窗口1,卖了第:1,还剩:9张票! 窗口1,卖了第:2,还剩:8张票! 窗口1,卖了第:3,还剩:7张票! 窗口2,卖了第:4,还剩:6张票! 窗口2,卖了第:5,还剩:5张票! 窗口2,卖了第:6,还剩:4张票! 窗口2,卖了第:7,还剩:3张票! 窗口2,卖了第:8,还剩:2张票! 窗口2,卖了第:9,还剩:1张票! 窗口2,卖了第:10,还剩:0张票! 售罄!!! 售罄!!!
同步方法
这里需要注意,在继承Thread的方法实现的线程的时候,这个方式是不好使的,因为他是用的对象锁是this
同步方法使用的对象锁是:this //同步方法 public synchronized int mySale() throws InterruptedException{ int i=0; if (num > 0) { sleep(500);// 卖一张票所需时间 num--; System.out.println(this.getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } else { System.out.println("售罄!!!"); // break; i=1; } return i; }
完整的同步
public class Ticket extends Thread { static int num = 10;// 票数10张,共享资源同步 private static Object obj = new Object(); // 静态的对象锁 全局变量 @Override public void run() { try { while (true) { // synchronized (obj) {//(参数) 对象锁 this new Object new Tikcet() synchronized (Ticket.class) {// Class 类型作为对象锁 if (num > 0) { sleep(500);// 卖一张票所需时间 num--; System.out.println(this.getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } else { System.out.println("售罄!!!"); break; } } // 调用同步方法 /* * int i = this.mySale(); if(i==1){ break; } */ } } catch (InterruptedException e) { e.printStackTrace(); } } // 同步方法 public synchronized int mySale() throws InterruptedException { int i = 0; if (num > 0) { sleep(500);// 卖一张票所需时间 num--; System.out.println(this.getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } else { System.out.println("售罄!!!"); // break; i = 1; } return i; } }
通过实现Runnable来进行线程的同步
这里因为Runnale只需要实例化一次,所以用this没有影响。推荐使用线程同步的时候使用Runnable方法
public class Ticket1 implements Runnable { int num = 10;// 票数10张,共享资源同步 Object obj = new Object(); // 静态的对象锁 全局变量 @Override public void run() { try { while (true) { // synchronized (obj) {//(参数) 对象锁 this new Object new Tikcet() // synchronized (Ticket1.class) {// Class 类型作为对象锁 /* * synchronized (this) {// Class 类型作为对象锁 if (num > 0) { * Thread.currentThread().sleep(500);// 卖一张票所需时间 num--; * System.out.println(Thread.currentThread().getName() + ",卖了第:" * + (10 - num) + ",还剩:" + num + "张票!"); } else { * System.out.println("售罄!!!"); break; } } */ // 调用同步方法 int i = this.mySale(); if (i == 1) { break; } } } catch (InterruptedException e) { e.printStackTrace(); } } // 同步方法 public synchronized int mySale() throws InterruptedException { int i = 0; if (num > 0) { Thread.currentThread().sleep(500);// 卖一张票所需时间 num--; System.out.println(Thread.currentThread().getName() + ",卖了第:" + (10 - num) + ",还剩:" + num + "张票!"); } else { System.out.println("售罄!!!"); // break; i = 1; } return i; } }
线程同步时通过继承Thread和实现Runnable接口,对象锁区别
继承Thread
共享资源,必须是静态的才能被共享;
能用的对象锁:Class类型,静态对象
实现Runnable接口
共享资源,普通成员;
能用的对象锁:Class类型,静态对象,普通对象,this ,同步方法
只要做线程同步,推荐使用实现Runnable接口
方法定义的完整格式
访问权限{public|default|protected|private} [final] [static] [synchronized] 返回值类型|void 方法名称(参数类型 参数名称,…..) [throws Exception1,Exception2]{
[return [返回值|返回调用处]] ; }
生产者消费者问题
wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

wait() notify() notifyAll() 是Object类中的方法!!! 共享资源为:库房 生成电视10台,卖电视10台,当库存为0时,生成电视线程开启,销售电视线程等待; 当生成电视,库存为4时,生成电视线程等待; // 仓库 public class Store { int num=0;//库存 //生成电视 public synchronized void product(int i) throws InterruptedException{//i 生成了第几台电视 if(num==4){//库存满,等待 this.wait(); } //生成一台电视耗时 Thread.currentThread().sleep(300); num++;//生产了一台电视 System.out.println(Thread.currentThread().getName()+",生成了第:"+i+"台电视,库存为:"+num); this.notify();//唤醒消费线程,消费(销售电视) } //销售电视 public synchronized void sale(int i) throws InterruptedException{ if(num==0){ this.wait(); } //销售一台电视耗时 Thread.currentThread().sleep(100); num--;//销售了一台电视 System.out.println(Thread.currentThread().getName()+",销售了第:"+i+"台电视,库存为:"+num); this.notify();//唤醒生产线程,生产电视 } } public class Product implements Runnable{ private Store s; public Product() { } public Product(Store s) { this.s = s; } @Override public void run() { try { for (int i = 1; i <=10; i++) { s.product(i); } } catch (InterruptedException e) { e.printStackTrace(); } } } public class Sale implements Runnable { private Store s; public Sale() { } public Sale(Store s) { this.s = s; } @Override public void run() { try { for (int i = 1; i <=10; i++) { s.sale(i); } } catch (InterruptedException e) { e.printStackTrace(); } } } public class Test { public static void main(String[] args) { //库房 Store s = new Store(); //生产者 Runnable pro=new Product(s); Thread tp=new Thread(pro, "生产者"); tp.start(); //消费者 Runnable sal=new Sale(s); Thread ts = new Thread(sal,"消费者"); ts.start(); } }