线程锁、定时器

时间:2021-08-19 03:58:49

一、线程同步安全锁(关键在于同步监听对象要一致,同步监听对象看作是锁,也就是说多个对象要使用的是同一把锁

1、synchronized同步代码块锁(继承类线程和实现接口类线程都适用)

语法结构:synchronized(同步对象){

   需要被锁住的代码(也就是容易发生线程安全的代码)

}

同步对象:指的是同步监听对象,看作是是一把锁

示例代码:

线程锁、定时器线程锁、定时器
 1 /**
 2  * 解决线程同步安全问题最关键的是要使用同一把锁,也就是同一监听对象。
 3  * 
 4  *
 5  */
 6 public class Test1 {
 7     public static void main(String[] args) {
 8         TicketThread t1 = new TicketThread("梓沐");
 9         TicketThread t2 = new TicketThread("盂梦");
10         TicketThread t3 = new TicketThread("哈哈");
11         t1.start();
12         t2.start();
13         t3.start();
14     }
15 
16 }
17 /**
18  * 解决线程安全问题方法之一:synchronized同步代码块
19  * 语法结构:
20  *         synchronized(同步对象){
21  *             需要被锁住的代码
22  *         }
23  *         同步对象:简单理解就是一把锁
24  *         注意:多个对象拿到的必须是同一把锁
25  *         我们在填充同步对象的时候,使用了new Object()也使用了this,发现都不可以,因为创建了3个对象
26  *        所以3个线程拿到的锁都不是同一把,而是3把不同的锁,所以是没有解决线程安全问题的
27  *        怎么解决呢?
28  *        答:使用字节码对象可以解决
29  *        字节码对象   简单理解   类型.class
30  *        以.java为后缀的文件   我们称为源文件
31  *        以.class为后缀的文件  我们称为字节码文件
32  *        把字节码文件放到jvm中     在jvm中它会存在一个字节码对象,就是你当前类型.class
33  *        字节码对象它是单例的,它在jvm中永远只会存在一份
34  *
35  */
36 public class TicketThread extends Thread{
37     static int num = 2000;
38     public TicketThread(String string) {
39         super(string);
40     }
41     @Override
42     public void run() {
43     //当synchronized锁住了整个while循环时,只有一个线程能进去,当切换到其它线程时,其他线程也不能进入,因此会只有一个窗口买票。
44             while(num>0){
45     /*
46      * /当synchronized锁住了while循环中的执行语句时,此时会出现0票、-1票,原因是ABC三个线程都能进入while循环,
47      * 然后当A线程进入执行语句后,执行语句被锁上,此时A线程打印1,然后解锁执行语句,C线程进入,锁上执行语句,C线程打印0,
48      * 然后解锁执行语句,B线程进入,然后锁上执行语句,此时,B线程打印-1,租后解锁执行语句。因此,解决的办法是在同步代
49      * 码块里面加上if判断语句。
50      */
51                 synchronized (TicketThread.class) {
52                     if(num>0){
53                         System.out.println(this.getName()+":"+num);
54                         num--;
55                     }
56             
57                 }
58             }
59     }
60 
61 }
View Code

 

 

2、synchronized同步方法

/**

* 同步方法相关的同步监听对象问题:

* 如果你的方法是一个实例方法,那你同步监听对象就是当前类的对象this

* 如果你的方法是类的方法,那你同步监听对象就是当前类的字节码对象

* 字节码对象在jvm中永远只有1份

* 如果你实现的多线程,你是使用的继承方式,那就不能使用同步方法(因为在*类方法里面不能使用实例方法,也就是说在继承方式实现多线程时,我们不能  *知道线程的名称,因此不能满足我们的需求)

*必须使用同步代码块,因为同步方法不能解决我们的需求

*/

示例代码:

线程锁、定时器线程锁、定时器
public class Test1 {
    public static void main(String[] args) {
        TicketThread t = new TicketThread();
        Thread t1 = new Thread(t,"梓沐");
        Thread t2 = new Thread(t,"盂梦");
        Thread t3 = new Thread(t,"哈哈");
        t1.start();
        t2.start();
        t3.start();
    }
}
//解决线程安全问题之synchronized同步方法
public class TicketThread implements Runnable{
    int num = 50;
    @Override
    public void run() {
        while(num>0){
            saleTicket();
        }
    }
    private synchronized void saleTicket() {
        if(num>0){
            System.out.println(Thread.currentThread().getName()+num);
            num--;
        }
/**
 * 同步方法相关的同步监听对象问题:
 *             如果你的方法是一个实例方法,    那你同步监听对象就是当前类的this
 *             如果你的方法是类的方法,那你同步监听对象就是当前类的字节码对象
 *             字节码对象在jvm中永远只有1份
 * 如果你实现的多线程,你是使用的继承方式,那就不能使用同步方法(因为在类方法里面不能使用实例方法,也就是说在继承方式实现多线程时,
 * 我们不能知道线程的名称,因此不能满足我们的需求)
 *必须使用同步代码块,因为同步方法不能解决我们的需求
 */    
    }
}
View Code

 

 

3、Lock加锁的方法(Lock是一个接口)

因为lock是一个接口,因此不能直接实例化,所以只能实例化它的子类ReentrantLock类

示例代码:

线程锁、定时器线程锁、定时器
public class Test1 {
    public static void main(String[] args) {
        TicketThread t1 = new TicketThread("梓沐");
        TicketThread t2 = new TicketThread("盂梦");
        TicketThread t3 = new TicketThread("哈哈");
        t1.start();
        t2.start();
        t3.start();
    }
}
/**解决线程安全问题:
*            解决线程同步方式三:使用Lock方式
*                语法格式:
*                    Lock lock = new ReentrantLock()   生成一个lock对象
*                    lock.lock();//上锁
*                    try{
*                        需要被锁住的代码
*                    }finally{
*                        lock.unlock();//释放锁资源
*                    }
*
*            线程同步前提: 多个线程要共享一把锁
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketThread extends Thread{
    static Lock lock = new ReentrantLock();
    static int num = 2000;
    public TicketThread(String string) {
        super(string);
    }
    @Override
    public void run() {
        while(num>0){
            lock.lock();
//同步监听对象就是lock对象
            try {
                if(num>0){
                    System.out.println(getName()+num);
                    num--;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
View Code

二、join方法

1、void join() 当某个线程加入之后,其他线程就必须等着,一直要等着加入的线程执行完毕之后,其他线程才能继续执行

三、守护线程

  1守护线程/精灵线程/后台线程:每个线程都可以或不可以标记为一个守护程序

   2后台线程仅仅就是对线程的一个分类或者标记。

   3后台线程作用:后台线程主要是给前台线程做服务的

   示例:其实垃圾回收线程就是一个后台线程

   4后台线程特点: 当前台线程死亡之后,后台线程会自动死亡(注意:如果前台线程死了之后,后台线程不一定马上死亡 (因为很可能处理后事))

   5后台线程相关的方法:

   boolean isDaemon() 测试该线程是否为守护线程。

    void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。

   6主线程:

   默认是前台线程  

   主线程只能是前台线程不能更改为后台线程

 

  注意:当一个线程处于运行状态的时候,是不能更改它为后台线程的

 

   7自定义线程:

   默认是后台线程还是前台线程是和它创建的环境线程完全一致

  线程的优先级是和创建它的环境线程是一致的

四、线程通信和唤醒线程

1、线程的生命周期(创建线程对象、启动线程、运行线程、死亡(线程执行完毕或者异常没处理好))

2、线程通信指的是线程之间共享资源

3、synchronized同步方法下的线程通信和唤醒线程

代码实例:

线程锁、定时器线程锁、定时器
public class Test1 {
    public static void main(String[] args) {
        Account account = new Account(0);
        SaveMoney saveMoney = new SaveMoney(account);
        GetMoney getMoney = new GetMoney(account);
        saveMoney.start();
        getMoney.start();
    }
}
public class SaveMoney extends Thread{
    private Account account;

    public SaveMoney(Account account) {
        super();
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            try {
                account.saveMoney(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
public class GetMoney extends Thread{
    private Account account;

    public GetMoney() {
        super();
    }

    public GetMoney(Account account) {
        super();
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            try {
                account.getMoney(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}
public class Account{
    private int money;
    private boolean empty = true;//定义一个字段来标志是否有钱
    public Account(int money) {
        super();
        this.money = money;
    }

    public Account(){
        
    }
    
    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public synchronized void saveMoney(int i) throws InterruptedException {
        if(!empty){
            this.wait();
        }
        System.out.println("存钱之前=========="+money);
        money += i;
        System.out.println("存钱之后=========="+money);
        empty = false;
        this.notify();
        
    }//两个方法的同步监听对象(锁)是Account类的实例对象。

    public synchronized void getMoney(int i) throws InterruptedException {
        if(empty){
            this.wait();
        }
        System.out.println("取钱之前=========="+money);
        money -= i;
        System.out.println("取钱之后=========="+money);
        empty = true;
        this.notify();
        
    }
    

}
View Code

4、lock方法下的线程通信和唤醒线程

示例代码:

线程锁、定时器线程锁、定时器
public class Test1 {
    public static void main(String[] args) {
        Account account = new Account(0);
        SaveMoney saveMoney = new SaveMoney(account);
        GetMoney getMoney = new GetMoney(account);
        Thread t1= new Thread(saveMoney);
        Thread t2 = new Thread(getMoney);
        t1.start();
        t2.start();
    }
}
public class SaveMoney implements Runnable{
    private Account account;
    
    public SaveMoney(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            account.saveMoney(1000);
        }
    }
}
public class GetMoney implements Runnable{
    private Account account;
    public GetMoney(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        for (int i = 1; i <=12; i++) {
            account.getMoney(1000);
        }
    }
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
    private int money;
    private boolean empty = true;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //返回一个与此lock实例一起使用的condition实例,Condition对象替换了Object类的wait和notify方法
    /*
     * 使用lock时,同步监听对象就是lock。
     * 线程通信与唤醒线程指的是synchronizesd同步方法中使用Object的wait方法(成为等待线程),
     * 或者notify方法(唤醒线程)
     * 或者lock方法中使用condition对象中的await方法和single方法。
     * 注意:notify方法和single方法都只能一次唤醒一个线程
     */
    public Account() {
    }
    public Account(int money) {
        this.money = money;
    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
    public void saveMoney(int i) {
        lock.lock();
        try {
            if(!empty){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("======存钱之前"+money);
            this.money+=i;
            System.out.println("======存钱之后"+money);
            empty = false;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
    public void getMoney(int i) {
        lock.lock();
        try {
            if(empty){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("=====取钱之前"+money);
            this.money-=i;
            System.out.println("=====取钱之后"+money);
            empty = true;
            condition.signal();
        } finally {
            condition.signal();
        }
    }
}
View Code

 

 

五、Timer定时器

代码实例:

线程锁、定时器线程锁、定时器
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;
public class Test1 {
    public static void main(String[] args) throws ParseException {
        //Timer() 创建一个新计时器。
        Timer timer = new Timer();
        //void schedule(TimerTask task, Date time)  安排在指定的时间执行指定的任务。 如果时间已过,启动之后马上执行
        String str = "2019-04-09 11:36:00";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        timer.schedule(new TimerTask(){
//此处使用了匿名内部类,这样可以少些很多代码。匿名内部类只能new接口或者抽象类,由于抽象类和接口不能实例化,所以实际上new的是匿名内部类的对象
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("定时炸弹");
            }
            
        }, sdf.parse(str));
        // void schedule(TimerTask task, Date firstTime, long period)  在指定的firstTime执行指定的task任务,
        //以后每个period时间重复执行,如果时间已过,启动之后,立刻执行,并且每隔period时间重复执行,不会把之前没有执行的代码执行。
        timer.schedule(new TimerTask(){

            @Override
            public void run() {
                System.out.println("我在指定时间执行");        
            }    
        }, sdf.parse(str), 2000);
        //void schedule(TimerTask task, long delay, long period)   延迟delay时间执行task任务,每隔period时间又重复执行task任务
        //如果时间已过,启动之后,立刻执行,并且每隔period时间重复执行,不会把之前没有执行的代码执行。
        timer.schedule(new TimerTask(){

            @Override
            public void run() {
                System.out.println("我在指定的延迟时间执行");    
            }
        }, sdf.parse(str), 2000);
        /*
         * void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 在firstTime的时间第一次执行task任务,以后每隔
         * period时间重复执行task任务, 如果时间已过,它会把之前没有执行的代码,全部执行一次
         */timer.scheduleAtFixedRate(new TimerTask(){
             @Override
            public void run() {
                System.out.println("我会把之前没执行过的代码全部执行");    
            }
         }, sdf.parse(str), 2000);
    }
}
View Code