线程

后台线程

在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();
    }

}
生产者,消费者案例

线程的生命周期

原文地址:https://www.cnblogs.com/taozizainali/p/10858159.html