多线程基础笔记

01简单入门

继承thread,实现runable接口
区别就是实现runable要先创建线程类对象,然后创建Thread类对象,把线程类的对象丢进线程类对象的形参位置即可.

rable r = new rable();
new Thread(t).start();

继承thread类不建议使用, 避免oop单继承局限性
推荐使用rabble接口,主要是灵活方便, 方便一个对象被多个线程使用

02简单并发问题

package book.duoxiancheng_KuangShenShuo;

public class bugtickets implements Runnable {
    //票数
    private int ticket=10;
    @Override
    public void run() {
        while (true){
            if (ticket<=0){
                break;
            }
            try {
                //模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread()+"--->拿到了第"+ticket--+"票");
        }
    }

    public static void main(String[] args) {
        bugtickets b = new bugtickets();
        new Thread(b,"小白").start();
        new Thread(b,"老黄").start();
        new Thread(b,"大刘").start();
    }
}

输出

"C:\Program Files\Java\jdk1.8.0_101\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=50414:D:\IntelliJ IDEA 2018.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_101\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar;D:\developresource\MyDemo\java_javaSE_bookdemo\target\classes;D:\my_java\maven_repository\commons-io\commons-io\2.6\commons-io-2.6.jar;D:\my_java\maven_repository\ant\ant\1.6.5\ant-1.6.5.jar;D:\my_java\maven_repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.36\tomcat-embed-core-9.0.36.jar;D:\my_java\maven_repository\org\apache\tomcat\tomcat-annotations-api\9.0.36\tomcat-annotations-api-9.0.36.jar" book.duoxiancheng_KuangShenShuo.bugtickets
Thread[小白,5,main]--->拿到了第10票
Thread[大刘,5,main]--->拿到了第9票
Thread[老黄,5,main]--->拿到了第8票
Thread[小白,5,main]--->拿到了第7票
Thread[大刘,5,main]--->拿到了第5票
Thread[老黄,5,main]--->拿到了第6票
Thread[小白,5,main]--->拿到了第4票
Thread[大刘,5,main]--->拿到了第3票
Thread[老黄,5,main]--->拿到了第2票
Thread[小白,5,main]--->拿到了第1票
Thread[大刘,5,main]--->拿到了第-1票
Thread[老黄,5,main]--->拿到了第0

发现问题: 多个线程操作同一个资源时线程不安全,数据非常乱

案例

  1. 模拟龟兔赛跑
  2. 首先有赛道距离, 然后要离终点越来越近
  3. 判断比赛是否结束
  4. 打印出胜利者
  5. 龟兔赛跑开始
  6. 兔子要睡觉
  7. 乌龟赢
package book.duoxiancheng_KuangShenShuo;

public class Race implements Runnable {
    //定义赛道
    private static String winner;
    @Override
    public void run() {
        //定义步数100
        for (int i = 0; i <= 100; i++) {
            //模拟兔子没10休息一次
            if (Thread.currentThread().getName().equals("兔") && i%10 == 0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //定义游戏输赢
            boolean flag = gameover(i);
           // /如果赢结束循环
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }

    private boolean gameover(int step) {
        //如果胜利者为空, 也就是没100步,返回false,继续循环
        if (winner!=null){
            return true;
        }else {
            if (step>=100){
                winner = Thread.currentThread().getName();
                System.out.println(winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race r= new Race();
        new Thread(r,"兔").start();
        new Thread(r,"龟").start();
    }
}

输出

"C:\Program Files\Java\jdk1.8.0_101\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=50734:D:\IntelliJ IDEA 2018.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_101\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar;D:\developresource\MyDemo\java_javaSE_bookdemo\target\classes;D:\my_java\maven_repository\commons-io\commons-io\2.6\commons-io-2.6.jar;D:\my_java\maven_repository\ant\ant\1.6.5\ant-1.6.5.jar;D:\my_java\maven_repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.36\tomcat-embed-core-9.0.36.jar;D:\my_java\maven_repository\org\apache\tomcat\tomcat-annotations-api\9.0.36\tomcat-annotations-api-9.0.36.jar" book.duoxiancheng_KuangShenShuo.Race
龟跑了0步
龟跑了1步
龟跑了2步
龟跑了3步
龟跑了4步
龟跑了5步
龟跑了6步
龟跑了7步
龟跑了8步
龟跑了9步
龟跑了10步
龟跑了11步
龟跑了12步
龟跑了13步
龟跑了14步
龟跑了15步
龟跑了16步
龟跑了17步
龟跑了18步
龟跑了19步
龟跑了20步
龟跑了21步
龟跑了22步
龟跑了23步
龟跑了24步
龟跑了25步
龟跑了26步
龟跑了27步
兔跑了0步
龟跑了28步
兔跑了1步
龟跑了29步
龟跑了30步
兔跑了2步
龟跑了31步
兔跑了3步
龟跑了32步
兔跑了4步
龟跑了33步
兔跑了5步
龟跑了34步
龟跑了35步
龟跑了36步
兔跑了6步
龟跑了37步
兔跑了7步
龟跑了38步
兔跑了8步
龟跑了39步
兔跑了9步
龟跑了40步
龟跑了41步
龟跑了42步
龟跑了43步
龟跑了44步
龟跑了45步
龟跑了46步
龟跑了47步
龟跑了48步
龟跑了49步
龟跑了50步
龟跑了51步
龟跑了52步
龟跑了53步
龟跑了54步
龟跑了55步
龟跑了56步
龟跑了57步
龟跑了58步
龟跑了59步
龟跑了60步
龟跑了61步
龟跑了62步
龟跑了63步
龟跑了64步
龟跑了65步
龟跑了66步
龟跑了67步
龟跑了68步
龟跑了69步
龟跑了70步
兔跑了10步
龟跑了71步
兔跑了11步
龟跑了72步
兔跑了12步
兔跑了13步
兔跑了14步
兔跑了15步
兔跑了16步
兔跑了17步
兔跑了18步
兔跑了19步
龟跑了73步
龟跑了74步
龟跑了75步
龟跑了76步
龟跑了77步
龟跑了78步
龟跑了79步
龟跑了80步
龟跑了81步
龟跑了82步
龟跑了83步
龟跑了84步
龟跑了85步
龟跑了86步
龟跑了87步
龟跑了88步
龟跑了89步
龟跑了90步
龟跑了91步
龟跑了92步
龟跑了93步
龟跑了94步
龟跑了95步
龟跑了96步
龟跑了97步
龟跑了98步
龟跑了99步
龟

Process finished with exit code 0

03callable

基本操作
1.实现callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务 ExecutorService er = Executors.newFixedThreadPool(3);
5.提交执行 Future submit = er.submit(c1);
6.获取结果 boolean rs1 = submit.get();
7.关闭服务 er.shutdown();

        //创建执行服务
        ExecutorService er = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> submit = er.submit(c1);
        Future<Boolean> submit1 = er.submit(c2);
        Future<Boolean> submit2= er.submit(c3);
        //获取结果
        boolean rs1 = submit.get();
        boolean rs2 = submit2.get();
        boolean rs3 = submit1.get();
        //关闭服务
        er.shutdown();

04 静态代理模式

简单来说就是通过类直接调用静态方法

05lamda表达式

只适用于只有一个抽象方法的接口,也就是所谓的函数式接口
new Thread( () -> System.out.println(“zzzzzzzzz”)).start();
优点,避免匿名内部类定义过多, 匿名内部类说白了就是只使用一次的类,可以使用lamda表达式简化

函数式接口
functional interface
定义1,任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口
定义2. 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

06 线程状态

创建,就绪,阻塞,运行,死亡

07 线程方法

setPriority()更改优先级
sleep()休眠
join()等待该线程终止
yield()暂停线程
isalive()测试线程是否处于活动状态

停止线程
不推荐使用jdk提供的方法,stop(),destory()
推荐让线程自己停下来
建议使用一个标志位进行终止,当flag=false ,则终止线程
代码

public class zzxc implements Runnable {

    //线程中定义县城提使用的标识
    private boolean flag = true;
    @Override
    public void run() {
        int i =0;
        //使用标识
        while (flag){
            System.out.println("ttt"+ i++);
        }
    }
    //停止方法
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        zzxc z = new zzxc();
        new Thread(z).start();
        for (int i = 0; i < 100; i++) {
            if (i == 900){
                //调用停止方法
                z.stop();
            }
        }
    }
}

线程休眠
sleep(时间),指定当前线程阻塞的秒数,毫秒值
sleep存在异常interrupteEX
sleep时间到达后线程进入就绪状态
sleep应用
sleep可以模拟网络延迟,倒计时等
每个对象都有一个锁,sleep不会释放锁

注意:模拟网络延迟可以放大问题的发生性,方便在并发环境下定为问题
倒计时代码

package book.duoxiancheng_KuangShenShuo;

import java.text.SimpleDateFormat;
import java.util.Date;

public class djs {
    public static void main(String[] args) throws InterruptedException {
        /*try {
            js();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        Date d=new Date(System.currentTimeMillis());
        while (true){
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(d));
            //更新当前时间
            d=new Date(System.currentTimeMillis());
        }
    }
    public static void js() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num --);
            if (num<=0){
                break;
            }
        }
    }
}

线程礼让
yield()
礼让线程,让当前正在执行的线程暂停,但是不阻塞
将线程从运行状态转化为就绪状态
让spu重新调度,礼让不一定成功

线程强制执行join
join合并线程,待此线程执行完成后,再执行其他线程, 其他线程阻塞
类似插队,很霸道

package book.duoxiancheng_KuangShenShuo;

public class testjoin implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        testjoin t = new testjoin();
        Thread thread = new Thread(t);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            if (i==200){
                thread.join();
            }
            System.out.println("main......"+i);
        }
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("join........"+i);
        }
    }
}

观测线程状态
new 新生,尚未启动的线程处于此状态
runnable 在jvm中执行的线程
blocked 被阻塞等待监视器锁定
waiting 等待另一个线程执行特定动作
timmed waiting 正在等待另一个线程执行动作到达指定等待时间的线程处于此状态
terminated
线程退出
代码

package book.duoxiancheng_KuangShenShuo;

public class testthread {
    public static void main(String[] args) throws InterruptedException {
        Thread thread =new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("//");
        });
        //获得状态
        Thread.State state = thread.getState();
        //输出状态
        System.out.println(state);

        //启动
        thread.start();
        state = thread.getState();
        System.out.println(state);

        //监听线程状态不终止
        while (state != Thread.State.TERMINATED){
            Thread.sleep(100);
            //更新状态
            state = thread.getState();
            System.out.println(state);
        }
    }
}


注意:线程结束之后调用start()会报错,线程死亡后不能再次启动

线程的优先级
通过setPriority(int)设置, getPriority()获取
优先级默认为1,10,5
注意,优先级意味着被cpu调度的概率低,并不一定不会被调用

守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕, 不用等待守护线程执行完毕
main线程,gc线程
通过setDaemon(true)设置守护线程, false是用户线程,默认都是false

线程同步
同一个资源,大家都想使用,就需要排队等待
线程同步其实就是一种等待机制,等待前面的线程使用完毕,后面的线程再使用

synchronized的锁机制
类比于排队等待上厕所, 锁就是厕所的锁, 进去一个上锁,下一个等待
这是为了确保不会出现数据访问的冲突问题
当一个线程获得对象的排他锁, 独占资源, 其他线程就必须等待,
存在问题: 性能下降
一个线程持有锁,会导致需要此锁的其他线程被挂起
多线程竞争下, 加锁,释放锁,会导致比较多的上下文切换和延时调度
如果,一个优先级高的线程等待优先级低的线程释放锁,会导致优先级倒置,引发性能问题

三大不安全案列

package book.duoxiancheng_KuangShenShuo;

public class demo_buy {
    public static void main(String[] args) {
       bugtickets dd = new bugtickets();
        new Thread(dd,"ad").start();
        new Thread(dd,"ad").start();
        new Thread(dd,"ad").start();
    }
}
class buytickets implements Runnable{

    private int ticketnums = 10;
    //关闭线程的方法
    boolean flag = true;
    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void buy() throws InterruptedException {
        if (ticketnums<=0){
            flag = false;
            return;
        }
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketnums--);
    }
}

分析:每个线程都有自己的工作内存,在自己的工作内存交互,内存控制不当会造成数据不一致
案例,不安全的取钱

package book.duoxiancheng_KuangShenShuo;

public class gobank {
    public static void main(String[] args) {
        account a = new account(100,"结婚基金");
        quqian q =new quqian(a,50,"你");
        quqian q1 =new quqian(a,100,"girlfrends");
       q.start();
       q1.start();
    }
}
class account{
    int ye;
    String name;

    public account(int ye, String name) {
        this.ye = ye;
        this.name = name;
    }
}
class quqian extends Thread{

    account accounts;
    int qumoney;
    int nowmonery;
    public quqian(account accounts,
            int qumoney,
            String name){
        super(name);
        this.accounts=accounts;
        this.qumoney=qumoney;

    }

    @Override
    public void run() {
        if (accounts.ye-qumoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        accounts.ye = accounts.ye-qumoney;
        nowmonery = qumoney+nowmonery;
        System.out.println(accounts.name+"余额"+accounts.ye);
        System.out.println(this.getName()+Thread.currentThread().getName()+"手里的钱"+nowmonery);
    }
}

分析通过sleep可以放大并发问题,保证代码的健壮性

案例线程不安全的集合list

package book.duoxiancheng_KuangShenShuo;

import java.util.ArrayList;
import java.util.List;

public class unsafelist {
    public static void main(String[] args) {
        List<String> list =new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

发现,长度小于预期
不安全的原因,开启多个线程往集合里面添加.元素,在同一个瞬间会有多条线程往同一个位置添加,这样就会覆盖掉先添加的元素,造成集合中的元素减少

同步方法

synchronized实现原理就是队列和锁
synchronized两种用法
synchroonized方法,synchronized块
synichorized方法控制对对象的访问,每个对象都有一把锁,每个synchroized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行就独占该锁,直到方法返回才释放锁,后面被阻塞的方法 才能获得该锁
缺点: 若是一个大的方法申明为sychnrocized则会影响效率

public synchronized void xxx(){}

上锁原则
方法里面需要修改的内容才上锁
只读的内容不需要上锁,浪费资源,可以使用sychronized块
同步方法,锁的是this,锁你要操作改变数据的方法

同步块 sycoronized(obj){}
obj是一个监视器,可以是任何对象,但是推荐使用共享资源作为监视器
比如银行取钱
可以

sychronized(account){
     执行的修改方法
     要锁变化的量,指需要增删改的对象
}
package book.duoxiancheng_KuangShenShuo;

import java.util.ArrayList;
import java.util.List;

public class unsafelist {
    public static void main(String[] args) {
        List<String> list =new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
               
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

juc下线程安全的list

package book.duoxiancheng_KuangShenShuo;

import java.util.concurrent.CopyOnWriteArrayList;

public class testJUC {

    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list =new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
                }).start();
        }
           try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

死锁

两个线程各自占有一些共享资源,互相等待对方占有 的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,
当一个同步块同时拥有两个以上对象的锁时,就有可能发生死锁问题
代码

package book.duoxiancheng_KuangShenShuo;

public class Deadlock {
    public static void main(String[] args) {
        Makelove ml=new Makelove(0,"w");
        Makelove m2=new Makelove(1,"w2");
        ml.start();
        m2.start();
    }
}
//王祖贤
class W{

}
//刘亦菲
class L{

}
class Makelove extends Thread{
    //需要的女只有一个,用static保证
    static W w =new W();
    static L l = new L();
    int choice;//选择
    String me;

    Makelove (int choice,String me){
        this.choice=choice;
        this.me = me;
    }
    @Override
    public void run() {
        makeloves();
    }
    //互相持有对方的锁
    private void makeloves(){
        if (choice ==0){
            //第一个锁,抱着王祖贤想要刘亦菲
            synchronized (w){
                //获得王祖贤
                System.out.println(this.me+"王祖贤");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (l){
                    System.out.println(this.me+"刘亦菲");//1s后获得刘亦菲
                }
            }
        }else {
            //第er个锁,抱着刘亦菲想要wang
            synchronized (l){
                //获得l
                System.out.println(this.me+"王祖贤");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (w){
                    System.out.println(this.me+"刘亦菲");//1s后获得刘亦菲
                }
            }
        }
    }
}

解决方法,不让对象持有对方的锁

lock锁

线程同步机制的一种,通过显式定义同步锁对象来实现
java.util.concurrent.locks.LOck接口时控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只能有一个线程对lock对象加锁,线程开始访问共享资源时,应先获得lock对象
ReentrantLock类实现了lock,比较常用, 可以显式加锁,释放锁

class A{
    private final ReentrantLock lock = new ReentrantLock();
    public void b(){
        lock.lock();
        try {
            //保证线程安全的代码
        }finally {
            lock.unlock();//释放锁
            //如果代码同步有异常,需要将unlock();写入finnally语句块中
        }
    }
}

sychronized和lock的对比
lock是显式的,需要手动开启手动释放,sychronized是隐式的,出了作用域自动释放
lock只有代码块锁,sychronized有代码块锁和方法锁
使用lock锁,jvm将花费更少的时间来调度线程,性能更好
优先使用顺序,
lock>同步代码块>同步方法

线程协作/通信

管程法,就是生产者消费者问题
线程通信的方法
wait(),表示线程会一直等待,直到其他线程通知,与sleep不同,wait()会释放锁
wait(long timeout) 指定需要等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyall() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的优先调用
注意:以上方法只能在同步方法和同步代码块中使用

管程法

生产者-缓冲区-消费者

信号灯法

生产者/消费者
需要定义一个标志位 flag=true/false

线程池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值