多线程

一. 进程和线程

1.1 进程

进程就是电脑中一个运行的软件,一个正在运行的程序

在做的一件事情就是一个进程

1.2 线程

线程理解为一个进程中步骤

我们在做一件事情时,分很多步骤,每一个步骤就是一个线程,同一时刻只能做一件事情

做菜是一个进程,买菜,洗菜,切菜,炒菜就是一个一个的线程。

注意:

进程间不能共享数据段地址,但同进程的线程之间可以。

  • QQ软件不能使用谷歌内存中数据,360不能使用QQ内存中的数据。
  • 同一个进程中的线程,可以贡献数据,会产生数据不安全的情况。

1.3 进程是如何执行的

java多线程给线程设置名字_就绪状态

 

java多线程给线程设置名字_System_02

 

二. 创建线程

2.1 继承Thread类

class 类 extends Thread {

    public void run() {
        //这个线程被cpu选中执行时,执行的业务代码
    }
}
run方法是使用当前类创建线程,被cpu选中时,实行的业务代码

package com.qfedu;

public class Demo01 {

    //main方法    就是一个线程(主线程)
    public static void main(String[] args) throws InterruptedException {
        
        MyThread mt = new MyThread();
        mt.start();   // 开启了一个线程,和主线程共同竞争cpu执行时间
        
        
        for (int i = 0; i < 100; i++) {
            System.out.println("main--"+i);
        }
        
    }
    
}

/*
 * 创建线程
 * 1. 创建类继承Thread
 * 2. 重写run方法              该线程执行的业务代码
 */
class MyThread extends Thread {
    
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("自定义线程--"+i);
        }
    }
    
}

2.2 实现Runnable接口

实现Runnable接口,重写run方法

class 类 implements Runnable {

    public void run() {
        //这个线程被cpu选中执行时,执行的业务代码
    }
}

Thread t = new Thread(类的对象);
package com.qfedu;

public class Demo02 {

    public static void main(String[] args) {
        
        Runnable runnable = new YourThread();
        
        Thread t = new Thread(runnable, "线程1");
        Thread t2 = new Thread(runnable, "线程2");
        
        t.start();    // 开启了一个线程,和主线程共同竞争cpu执行时间
        t2.start();   // 开启了一个线程,和线程1共同竞争cpu执行时间
    }
}

/*
 * 实现Runnable接口
 * 重写run方法              该线程执行的业务代码
 */
class YourThread implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+i);
        }
    }
    
}

三. 线程的状态

基本状态:

新建状态:创建线程对象

就绪状态:线程对象执行start()方法

运行状态:被cpu选中,执行run方法,如果在分配的时间片内运行结束,进入死亡状态,如果没有运行 结束,回到就绪状态。

死亡状态:run方法执行结束,进入死亡状态

java多线程给线程设置名字_开发语言_03

 

package com.qfedu;

public class Demo03 {

    public static void main(String[] args) {
        
        //1. 新建状态
        System.out.println("新建状态");
        HerThread mt = new HerThread();
        
        /*
         * 2. 就绪状态      
         * 
         * 这个线程可以和这个进程中的其他线程,共同竞争cpu的运行时间
         * 
         * 如果竞争到了,就执行它的run方法中代码     会进入运行状态
         * - 如果在运行的时间片段内,run方法没有执行结束,那么就回到就绪状态
         * - 如果run方法执行结束,该线程就over了
         */
        System.out.println("就绪状态");
        mt.start();
    }
    
}

class HerThread extends Thread {
    
    @Override
    public void run() {
        System.out.println("运行状态");
        
        System.out.println("死亡状态");
    }
    
}

四. 卖票实例

  • 四个窗口各卖100张票
  • 四个窗口共卖100张票
package com.qfedu;

public class Demo04 {

    /*
     * 四个窗口,每个窗口各卖100张票
     */
    public static void main(String[] args) {
        MyTicket t1 = new MyTicket("窗口1");
        MyTicket t2 = new MyTicket("窗口2");
        MyTicket t3 = new MyTicket("窗口3");
        MyTicket t4 = new MyTicket("窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        
    }
}

class MyTicket extends Thread {
    
    private int count = 100;
    
    public MyTicket(String name) {
        super(name);
    }
    
    public void run() {
        
        while(true) {
            
            if(count <= 0) {
                break;
            }
            
            System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            
        }
        
    }
    
    
}
package com.qfedu;

public class Demo04_2 {

    /*
     * 四个窗口,共卖100张票
     */
    public static void main(String[] args) {
        Runnable runnable = new YourTicket();
        
        Thread t1 = new Thread(runnable, "窗口1");
        Thread t2 = new Thread(runnable, "窗口2");
        Thread t3 = new Thread(runnable, "窗口3");
        Thread t4 = new Thread(runnable, "窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        
    }
    
}

class YourTicket implements Runnable {
    
    private int count = 100;

    @Override
    public void run() {
        
        while(true) {
            
            if(count <= 0) {
                break;
            }
            System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            
        }
        
    }
    
}

五. 线程常用方法

//休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
public static native void sleep(long millis) throws InterruptedException;
//加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
public final void join() throws InterruptedException {}

//主动放弃cpu资源,回到就绪状态
public static native void yield();
//设置线程调用优先级  1-10   默认是5
public final void setPriority(int newPriority) {}
//是否是守护线程,非守护线程执行结束   守护线程自动结束
public final void setDaemon(boolean on) {}
线程在执行sleep(long millis) , join() 后进入等待状态

线程在执行yield()后,回到就绪状态

package com.qfedu;

public class Demo05 {

    public static void main(String[] args) throws InterruptedException {
        
        for(int i=1; i<100; i++) {
            
            /*
             * 休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
             * 
             * 在等待的过程中,是不释放锁的
             */
            Thread.sleep(1000);
            
            System.out.println("吃西瓜,现在是第" + i +"个");
        }
        
    }
    
}


package com.qfedu;

public class Demo06 {

    /*
     * 30个桃子
     * 
     * 20个西瓜
     * 
     * 开始随机的吃,如果桃子已经吃了10个,还有西瓜,那就先把西瓜吃完,才能吃桃子
     * 
     * 
     * 随机  改成  顺序
     */
    public static void main(String[] args) throws InterruptedException {
        
        EatWatermelon t = new EatWatermelon();
        t.start();
        
        
        for(int i=1; i<=30; i++) {
            System.out.println("吃了第"+i+"个桃子");
            
            if(i == 10) {
                 //加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
                t.join();  
            }
            
        }
        
    }
    
}

//吃西瓜线程
class EatWatermelon extends Thread {
    
    @Override
    public void run() {
        
        for(int i=1; i<=20; i++) {
            System.out.println("吃了第"+i+"个西瓜");
        }
        
    }
    
}


package com.qfedu;

public class Demo07 {

    public static void main(String[] args) {
        
        YieldThread yt = new YieldThread();
        yt.start();
        
        for(int i=1; i<=100; i++) {
            
            System.out.println("main--"+i);
            
            if(i%3 == 0) {
                Thread.yield(); // 如果是3的倍数,就主动放弃cpu资源,回到就绪状态
            }
        }
    }
}

class YieldThread extends Thread {
    
    @Override
    public void run() {
        
        for(int i=1; i<=100; i++) {
            
            System.out.println("YieldThread--"+i);
            
            if(i%8 == 0) {
                yield();  // 如果是8的倍数,就主动放弃cpu资源,回到就绪状态
            }
            
        }
    }
    
}


package com.qfedu;

public class Demo08 {

    public static void main(String[] args) {
        DaemonThread t1 = new DaemonThread();
        t1.setName("非守护线程");
        t1.setPriority(10);
        
        DaemonThread t2 = new DaemonThread();
        t2.setName("守护线程");
        t2.setDaemon(true);
        t2.setPriority(1);
        
        t1.start();
        t2.start();
    }
}

class DaemonThread extends Thread {
    
    @Override
    public void run() {
        for(int i=1; i<=100; i++) {
            System.out.println(getName() + "--"+i);
        }
    }
}

六. 线程安全

一个进程中的多个线程在执行的过程中可能会对一个变量进行操作,这样就有可能出现数据混乱的问题,可以通过对程序加锁来解决这种情况。

synchronized (对象) {
    //代码块
}
被加锁的代码,在同一时刻只能由一个线程执行

想要执行加锁的代码,必须 获取 对象的锁

package com.qfedu;

public class Demo09 {

    public static void main(String[] args) {
        
        Runnable runnable = new HerTicket();
        
        Thread t1 = new Thread(runnable, "窗口1");
        Thread t2 = new Thread(runnable, "窗口2");
        Thread t3 = new Thread(runnable, "窗口3");
        Thread t4 = new Thread(runnable, "窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}


class HerTicket implements Runnable {
    
    private int count = 100;
    
    Object o = new Object();

    @Override
    public void run() {
        
        while(true) {
            
            //对代码块进行加锁,这个代码块在一个时刻,只能由一个线程执行
            synchronized (o) {
                if(count <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            }
            
            
        }
        
    }
    
}

线程安全,代码同步

synchronized(对象) {
    //代码
}

public synchronized 返回类型 方法名(参数列表) {
    //代码块
}
  • 加锁的对象,可以是任意对象
  • 线程必须获取该对象的锁,才能执行加锁后的代码
  • 在执行完加锁后的代码后,会立即释放锁
  • 可以对代码块进行代码块,也可以对方法进行加锁

代码块加锁

package com.qfedu;

public class Demo01 {

    public static void main(String[] args) {
        Runnable runnable = new Ticket();
        
        for(int i=1; i<=4; i++) {
            Thread t = new Thread(runnable,"第"+i+"窗口");
            t.start();
        }
        
    }
}

class Ticket implements Runnable {
    
    int count = 100;

    @Override
    public void run() {
        
        while(true) {
            
            synchronized (this) {
                
                //如果没票,就不卖了
                if(count <= 0) {
                    break;
                }
                
                count--;
                System.out.println(Thread.currentThread().getName()+(100-count)+"张票");
            }
            
        }
        
    }
    
}

方法加锁

  • 关键字定义在方法上
  • 默认加锁的是该类对象
package com.qfedu;

public class Demo02 {

    public static void main(String[] args) {
        Runnable runnable = new Ticket2();
        
        for(int i=1; i<=4; i++) {
            Thread t = new Thread(runnable,"第"+i+"窗口");
            t.start();
        }
        
    }
}

class Ticket2 implements Runnable {
    
    int count = 100;

    @Override
    public void run() {
        
        while(true) {
            boolean r = sell();
            if(r) {
                break;
            }
        }
        
    }
    
    
    /*
     * 如果加锁的代码是整个方法体,且加锁的对象为当前对象
     * 可以关键字synchronized定义在方法上
     */
    public synchronized boolean sell() {
        //如果没票,就不卖了
        if(count <= 0) {
            return true;  //没票了
        }
        
        count--;
        System.out.println(Thread.currentThread().getName()+(100-count)+"张票");
        
        return false;    //不是没票了
    }
    
}

常见加锁类

  • StringBuffer
  • Vecotr
  • Hashtable
package com.qfedu;

public class Demo03 {


    /*
     * 开启10个线程,每个线程向同一个StringBuilder对象中添加100个字符
     * 
     * 
     * 开启10个线程,每个线程向同一个ArrayList/Vector对象中添加100个整数,每次打印元素个数
     */
    
    public static void main(String[] args) {
        
        Runnable runnable = new MyThread();
        
        for(int i=1; i<=10; i++) {
            Thread t = new Thread(runnable,"第"+i+"窗口");
            t.start();
        }
        
    }
}

class MyThread implements Runnable {
    
    StringBuffer sf = new StringBuffer();

    @Override
    public void run() {
        for(int i=1; i<=1000; i++) {
            sf.append("1");
        }
        
        System.out.println(sf.length());
    }   
}

死锁

  • 两个线程都拥有对方执行所需要的锁,但是又无法释放当前的锁,导致两个线程都处于阻塞状态,无法继续执行,称之为死锁。
package com.qfedu;

public class Demo04 {

    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        
        t1.start();
        t2.start();
    }
}

class MyLock {
    //创建两个对象作为锁
    static Object a = new Object();
    static Object b = new Object();
}


class T1 extends Thread {
    
    @Override
    public void run() {
        
        synchronized (MyLock.a) {
            System.out.println("我获取到a了");
            
            synchronized (MyLock.b) {
                System.out.println("我获取到b了");  //必须获取b锁,才能执行,释放a锁
            }
            
        }
        
    }
}

class T2 extends Thread {
    
    @Override
    public void run() {
        
        synchronized (MyLock.b) {
            System.out.println("我获取到b了");
            
            synchronized (MyLock.a) {
                System.out.println("我获取到a了"); //必须获取a锁,才能执行,释放b锁
            }
            
        }
        
    }
}

七. 线程通信

1. 主要方法

//等待,一直等待被唤醒
public final void wait() throws InterruptedException {
        wait(0);
}

//等待固定时间数
public final native void wait(long timeout) throws InterruptedException;

//唤醒所有线程
 public final native void notifyAll();

//唤醒一个线程
 public final native void notify();
  • 如果synchronized定义在方法上,那么获取的对象锁,就是该对象。
  • wait()是Object类的方法,表示使当前线程进入等待状态,意思就是程序不再往下执行了,等待被唤醒。
  • notifyAll(),唤醒所有执行wait()方法处于等待状态的线程,继续往下执行
  • notify(),唤醒一个执行wait()方法处于等待状态的线程,继续往下执行

2. 生产者消费者实例

包子铺

  • 包子
  • 蒸笼

生产者和消费者模式

  • 包子
  • 包子笼
  • 最多放6个包子
  • 张师傅,劳师傅,王师傅 往包子笼放包子
  • 首先判断,包子笼有没有放满,如果放满了,等着
  • 没放满,继续放
  • 让他们三个拿包子
  • 小明,小红,小芳从包子笼拿包子
  • 首先判断,包子笼有没有包子,如果没有了,等着
  • 如果有,就拿包子
  • 让师傅们继续放

使用程序实现上述的实际场景:

  1. 创建包子类
  1. id,productName
  1. 创建包子笼类
  1. 放包子
  2. 取包子
  1. 创建生产者线程
  2. 创建消费者线程

包子类:

package com.qfedu;

//包子类
public class Bun {

    private Integer id; // 编号
    
    private String productName;  //谁做的
    
    public Bun() {
        // TODO Auto-generated constructor stub
    }

    public Bun(Integer id, String productName) {
        super();
        this.id = id;
        this.productName = productName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }
    
}

包子笼类:

package com.qfedu;

//包子笼
public class BunCage {

    //存放包子的数组
    Bun[] cage = new Bun[6];
    
    //包子笼中包子的个数
    int size = 0;
    
    
    //放包子
    public synchronized void putBun(Bun bun) {
        
            //判断是否放满了
            while(size >=6) {  //每次被唤醒后,还需要继续判断是否已放满
                
                try {
                    this.wait();  //进入等待状态,必须被唤醒,才能继续执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
            
            //放包子操作
            cage[size] = bun;
            size++;
            System.out.println(Thread.currentThread().getName()+"放了第"+bun.getId()+"包子");
            
            //唤醒拿包子
            this.notifyAll();  //唤醒所有的线程
    }
    
    //拿包子
    public synchronized void getBun() {
        
        //判断是否还有包子
        while(size <=0) {  //每次被唤醒后,还需要继续判断是否有包子
            
            try {
                this.wait(); //进入等待状态,必须被唤醒,才能继续执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
        // size = 5;  4
        
        size--;
        Bun bun = cage[size];
        cage[size] = null;
        System.out.println(Thread.currentThread().getName()+"拿了第"+bun.getId()+"个包子");
        
        //唤醒放包子
        this.notifyAll();  //唤醒所有的线程
    }
    
}

生产者线程:

package com.qfedu;

/*
 * 包子生产者,放包子
 * 
 */
public class Product implements Runnable{
    
    //包子笼
    BunCage bunCage;
    
    

    @Override
    public void run() {
        
        //每个人,放30个包
        for(int i=1; i<=30; i++) {
            //放包子
            bunCage.putBun(new Bun(i,Thread.currentThread().getName()));
        }
        
    }

}

消费者线程:

package com.qfedu;

//消费者消费包子
public class Consumer implements Runnable{
    
    //包子笼
    BunCage bunCage;

    @Override
    public void run() {
        
        //每个人吃三十个
        for(int i=1; i<=30; i++) {
            //吃包子
            bunCage.getBun();
        }
        
    }

}

测试:

package com.qfedu;

public class BunTest {

    public static void main(String[] args) {
        
        //一个包子笼
        BunCage cage = new BunCage();
        
        //生产者
        Product product = new Product();
        product.bunCage = cage;  //往这个笼子里放
        
        //消费者
        Consumer consumer = new Consumer();
        consumer.bunCage = cage; //往同一个笼子里取
        
        //创建生产者
        Thread p1 = new Thread(product, "张师傅");
        Thread p2 = new Thread(product, "劳师傅");
        Thread p3 = new Thread(product, "王师傅");
        
        
        //创建消费者
        Thread c1 = new Thread(consumer, "小明");
        Thread c2 = new Thread(consumer, "小红");
        Thread c3 = new Thread(consumer, "小芳");
        
        p1.start();
        p2.start();
        p3.start();
        c1.start();
        c2.start();
        c3.start();
    }
    
}

3. wait()和sleep()的区别

  • wait是等待,sleep是休眠。
  • wait()是Object类的方法,所有对象都可以使用,sleep是线程对象的方法。
  • 线程执行wait()进入等待状态,会释放锁,而执行sleep()进入等待状态,不会释放锁。

二. 线程池

1. 固定长度

语法:返回的是线程执行服务对象,需要调用submit()定义执行的业务逻辑

每执行一次submit()就需使用一个线程对象执行相应的业务逻辑

ExecutorService es = Executors.newFixedThreadPool(线程数);
package com.qfedu;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo01 {

    public static void main(String[] args) {
        
        /*
         * 1. 指定长度的线程池 一定指定了具体长度的线程,就不可改变了
         * 
         * 这个池里有固定多的线程对象,可以重复使用
         * 
         * 1.1 submit()  让线程池中的线程执行指定的业务逻辑
         */
        ExecutorService es = Executors.newFixedThreadPool(3); //3个线程对象
        
        for(int i=1; i<=4; i++) {
            //每执行一次submit()  就开启一个任务
            es.submit(new MyThread());
        }
        
        //主动的关闭线程池
        es.shutdown();
    }
    
}

class MyThread implements Runnable {

    @Override
    public void run() {
        for(int i=1; i<=20;i++) {
            System.out.println(Thread.currentThread().getName()+"******"+i);
        }
    }
    
}

2. 可变长度

语法:


ExecutorService es = Executors.newCachedThreadPool();


执行多少次submit()就开启多少个线程执行

package com.qfedu;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo02 {

    public static void main(String[] args) {
        
        /*
         * 2. 创建可变长度的线程池
         * 
         * 需要多少有多少
         * 
         * 每submit()一次就创建一个对象执行一次任务
         */
        ExecutorService es = Executors.newCachedThreadPool();
        
        
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for(int i=1; i<=20;i++) {
                    System.out.println(Thread.currentThread().getName()+"******"+i);
                }
            }
        };
        
        for(int i=1; i<=6; i++) {
            //
            es.submit(runnable);
        }
        
        es.shutdown();
    }
}

3. 单个长度

语法:


ExecutorService es = Executors.newSingleThreadExecutor();


线程池中只要一个线程

package com.qfedu;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo03 {

    public static void main(String[] args) {
        
        /*
         * 3. 创建单线程线程池
         * 线程池中只有一个线程
         */
        ExecutorService es = Executors.newSingleThreadExecutor();
    
        es.submit(new Runnable() {
            
            @Override
            public void run() {
                for(int i=1; i<=20;i++) {
                    System.out.println(Thread.currentThread().getName()+"******"+i);
                }
            }
        });
        
        es.shutdown();
        
    }
}

4. 可定时延迟线程池

语法:

//指定长度线程池
ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);

//延迟执行一次
ses.schedule(runnable, 3, TimeUnit.SECONDS);

//规定第一次延迟时间及每隔一段时间执行一次  0:第一次延迟时间   3:周期
ses.scheduleAtFixedRate(runnable, 0, 3, TimeUnit.SECONDS);
package com.qfedu;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo04 {

    public static void main(String[] args) {
        
        /*
         * 创建每个一段时间执行的线程池
         */
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
        
        Runnable runnable = new Runnable() {
            @Override
            public void run() {   //业务需求
                System.out.println("呵呵哒");
            }
        };
        
        //使用线程池中一个线程规定时间后执行一次该业务需求
//      ses.schedule(runnable, 3, TimeUnit.SECONDS);
        
        //0:首次执行的延迟时间    3:周期(每隔一段时间)
        ses.scheduleAtFixedRate(runnable, 0, 3, TimeUnit.SECONDS);
        
    }
}

三. Callable

在执行一个线程后,想到得到一个返回值,则需要使用Callable接口

package com.qfedu;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo05 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        
        Future<Integer> future = es.submit(new MyCall());
        
        //获取线程的返回值
        Integer result = future.get();
        
        System.out.println(result);
    }
}

class MyCall implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i=1; i<=100; i++) {
            sum+=i;
        }
        
        return sum;
    }
    
}


package com.qfedu;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo05_2 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        /*
         * 开启两个线程计算1+1000;
         */
        
        
        //创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        
        //开启一个线程计算1+500的和
        Future<Integer> future = es.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i=1; i<=500; i++) {
                    sum+=i;
                }
                return sum;
            }
            
        });

        
        //开启一个线程计算501+1000的和
        Future<Integer> future2 = es.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i=501; i<=1000; i++) {
                    sum+=i;
                }
                return sum;
            }
            
        });
        
        
        int result = future.get()+future2.get();
        System.out.println(result);
    }
}

四. Lock

Lock lock = new ReentrantLock();

lock.lock();  //上锁

lock.unlock();  //开锁
package com.qfedu;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo01 {
    
    /*
     * Lock锁
     */

    public static void main(String[] args) {
        Runnable runnable = new Ticket();
        
        for(int i=1; i<=4; i++) {
            Thread t = new Thread(runnable,"第"+i+"窗口");
            t.start();
        }
        
    }
}

class Ticket implements Runnable {
    
    //创建一个锁
    Lock lock = new ReentrantLock();
    
    int count = 100;

    @Override
    public void run() {
        
        while(true) {
            lock.lock();   //加锁
                
            //如果没票,就不卖了
            if(count <= 0) {
                break;
            }
            
            count--;
            System.out.println(Thread.currentThread().getName()+(100-count)+"张票");
            
            lock.unlock(); //解锁
        }
        
    }
    
}
synchronized 和Lock的区别

synchronized 是关键字,Lock是对象

当出现异常时,synchronized 会自动释放锁,Lock就不会释放锁,所以无论如何都要执行unlock方法

package com.qfedu;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo01 {
    
    /*
     * Lock锁
     */

    public static void main(String[] args) {
        Runnable runnable = new Ticket();
        
        for(int i=1; i<=4; i++) {
            Thread t = new Thread(runnable,"第"+i+"窗口");
            t.start();
        }
        
    }
}

class Ticket implements Runnable {
    
    //创建一个锁
    Lock lock = new ReentrantLock();
    
    int count = 100;

    @Override
    public void run() {
        
        while(true) {
            try {
                lock.lock();
                    
                //如果没票,就不卖了
                if(count <= 0) {
                    break;
                }
                
                count--;
                System.out.println(Thread.currentThread().getName()+(100-count)+"张票");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();  //保证锁一定会打开
            }
            
            
        }
        
    }
    
}

1. 读写锁

读 读 不互斥

读 写 互斥

写 写 互斥

package com.qfedu;

import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class ReadWriteDemo {
    
    /*
     * 当多个线程操作同一个ReadWriteDemo对象时
     * 
     * 如果一个线程正在执行getValue()方法,其他线程都不能执行读和写的方法
     * 
     * 读    读     互斥    不应该互斥
     * 读    写     互斥
     * 写    写     互斥
     */
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
    //读锁 
    ReadLock readLock = rwl.readLock();
    //写锁
    WriteLock writeLocl = rwl.writeLock();

    //属性
    private String value;
    
    //读
    public String getValue() {
        
        try {
            readLock.lock();  //读锁
            return value;
        }finally {
            readLock.unlock();
        }
    }
    
    //写
    public void setValue(String value) {
        writeLocl.lock();
        
        this.value = value;
        
        writeLocl.unlock();
        
    }
    
}