线程与线程池笔记(一)线程、同步锁、线程间通信简单使用

本篇主要简单做个笔记

  • 线程
  • 同步锁
  • 线程间通信
线程简单用法:
 Thread(object : Runnable{
            override fun run() {
                Log.i(TAG,"Runnable running in Thread:"+ Thread.currentThread())
            }
        }).start()
 Thread({
            Log.i(TAG,"Runnable lambda running in Thread:"+ Thread.currentThread())
        }).start()
object :Thread(){
            override fun run() {
                Log.i(TAG,"Thread running in Thread:"+ currentThread())
            }
        }.start()

那Thread和Runnable有什么区别呢?
我们发现:

class Thread implements Runnable {

Thread是继承Runnable接口的
有好几个构造方法:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }


最终都是往init里去

/*
 *
 */
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        Thread parent = currentThread();//先拿到当前线程
        if (g == null) {
            g = parent.getThreadGroup();//获取线程组
        }

        g.addUnstarted();
        this.group = g;

        this.target = target;//target就是Runnable对象
        this.priority = parent.getPriority();
        this.daemon = parent.isDaemon();
        setName(name);//设置我们要创建的线程名字

        init2(parent);//维护线程关系

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;//新线程的堆栈大小
        tid = nextThreadID();//父进程指定线程ID
    }

也就是对线程的各种初始化
那只好看start()方法了:

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        // Android-changed: throw if 'started' is true
        if (threadStatus != 0 || started)//保证一个线程不多次启动
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

nativeCreate(this, stackSize, daemon),接下来就是本地函数干的事了我们不追究了。daemon是是否设置守护线程的标志。
我们看一下run()方法:

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

只是执行了我们重写的run()方法,这就是start()启动和run()启动的区别
start()真正实现了多线程运行。run()则还是在主线程中运行。

Runnable和Thread的区别

继承Thread:重写run方法,start()执行run()

  • 优点:简单
  • 缺点:由于java只能单继承,所以当类已经有父类,就无法在继承Thread

实现Runnable:传入runnable引用,赋值给成员变量, start()时判断成员变量是否为null,然后在执行

  • 优点:可以多实现
  • 缺点:无法直接使用Thread类中方法,使用时需要先获取线程对象,才能使用Thread方法,代码复杂

以上是简简单单的创建线程,但是执行时会发现都会出现安全问题
同步问题

public class MyClass {
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.print(1);
            System.out.print(2);
            System.out.print(3);
            System.out.println(4);
        }
    }

}

我们的预期输出应该是:

1234
1234
1234

然而它是这样的:

A:\Android\AndroidStudio\jre\bin\java.exe -javaagent:A:\Android\AndroidStudio\lib\idea_rt.jar=10393:A:\Android\AndroidStudio\bin -Dfile.encoding=UTF-8 -classpath A:\Android\AndroidStudio\jre\jre\lib\charsets.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\access-bridge-64.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\cldrdata.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\dnsns.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\jaccess.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\localedata.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\nashorn.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\sunec.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\sunjce_provider.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\sunmscapi.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\sunpkcs11.jar;A:\Android\AndroidStudio\jre\jre\lib\ext\zipfs.jar;A:\Android\AndroidStudio\jre\jre\lib\jce.jar;A:\Android\AndroidStudio\jre\jre\lib\jsse.jar;A:\Android\AndroidStudio\jre\jre\lib\management-agent.jar;A:\Android\AndroidStudio\jre\jre\lib\resources.jar;A:\Android\AndroidStudio\jre\jre\lib\rt.jar;A:\Android\AndroidWorkspace\Actiondispatch\Test\build\classes\java\main com.example.test.MyClass
12311234
234
4
Process finished with exit code 0

我们希望一个方法体执行完全后才会执行另一个方法,也就是说线程执行一段代码时,cpu不会切回线程。这就需要同步锁
1、同步代码块:

public class MyClass {
    static Object o = new Object();
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            synchronized (o){
                System.out.print(1);
                System.out.print(2);
                System.out.print(3);
                System.out.println(4);
            }
        }
    }
}

记住这个同步对象锁不能是匿名对象,要不然没效果,这个锁对象随便,反正是对象就行
同步方法

public class MyClass {
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            print();
        }
    }
    public static synchronized void  print() {
        System.out.print(1);
        System.out.print(2);
        System.out.print(3);
        System.out.println(4);
    }
}

那这个锁是什么呢? 其实就是this对象。与同步方法块代码改动有点大,原因时不能在线程中直接用同步方法,不然每一个线程锁都是自己的对象,同步就不起作用了

  • 如果这个方法是静态的,那么锁对象默认就是类的字节码对象(万能锁对象)
  • 如果这个方法是非静态的,那么锁对象默认就是类的this对象

共享问题
先看下面代码

public class MyClass {
    static Object o = new Object();
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
    }
    static class MyThread implements Runnable {
         int tickets = 10;
        @Override
        public synchronized void  run() {
                while (true){
                    if (tickets<0) break;
                    System.out.println(Thread.currentThread().getId()+"-----"+tickets--+"tickets was sold");
                }
        }
    }
}
12-----10tickets was sold
12-----9tickets was sold
12-----8tickets was sold
14-----10tickets was sold
13-----10tickets was sold
14-----9tickets was sold
14-----8tickets was sold
14-----7tickets was sold
12-----7tickets was sold
14-----6tickets was sold
14-----5tickets was sold
13-----9tickets was sold
14-----4tickets was sold
12-----6tickets was sold
14-----3tickets was sold
14-----2tickets was sold
13-----8tickets was sold
13-----7tickets was sold
14-----1tickets was sold
12-----5tickets was sold
12-----4tickets was sold
14-----0tickets was sold
13-----6tickets was sold
12-----3tickets was sold
12-----2tickets was sold
13-----5tickets was sold
12-----1tickets was sold
13-----4tickets was sold
12-----0tickets was sold
13-----3tickets was sold
13-----2tickets was sold
13-----1tickets was sold
13-----0ticketits was sold

发现同一张票就被卖了多次,这是应为每一个线程都有自己的ticketits,怎么办呢,我只需在变量上加静态关键字就行了

public class MyClass {
    static Object o = new Object();
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
        new Thread(new MyThread()).start();
    }
    static class MyThread implements Runnable {
        static int tickets = 10;
        @Override
        public synchronized void  run() {
                while (true){
                    if (tickets<0) break;
                    System.out.println(Thread.currentThread().getId()+"-----"+tickets--+"tickets was sold");
                }
        }
    }
}

12-----9tickets was sold
13-----8tickets was sold
14-----10tickets was sold
13-----6tickets was sold
12-----7tickets was sold
12-----3tickets was sold
13-----4tickets was sold
14-----5tickets was sold
13-----1tickets was sold
12-----2tickets was sold
14-----0tickets was sold

需要注意的是这个同步锁还是需要的
看下一段代码

package com.example.test;

import java.io.UnsupportedEncodingException;

public class MyClass {
    static Object o = new Object();
    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
    static class MyThread implements Runnable {
        int tickets = 1000;//这种写法也会共享这个变量
        @Override
        public synchronized void  run() {
                while (true){
                        if (tickets<0) break;
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getId()+"-----"+tickets--+"tickets was sold");
                   

                }
        }
    }
}

因为tickets需要足够大再能分辨出我要讲的效果
所以就不粘结果了,这个结果是所有的打印全是在一个线程打印!为什么呢?再看以下是在不同线程打印的代码

package com.example.test;

import java.io.UnsupportedEncodingException;

public class MyClass {
    static Object o = new Object();
    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
    static class MyThread implements Runnable {
        int tickets = 1000;
        @Override
        public void  run() {
                while (true){
                    synchronized(MyThread.class){
                        if (tickets<0) break;
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getId()+"-----"+tickets--+"tickets was sold");
                    }

                }
        }
    }
}

所以说这里 while(true)最好不要放在锁里,否则全循环完才释放锁

两个线程间通信
通过Object类的wait()线程休眠和notify()随机唤醒另一条线程
Sychronized+wait+notify

public class MyClass {
    public static void main(String[] args) {
        final Print print = new Print();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printA();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printB();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread().start();
    }

}

class Print{
    int flag = 1;
    public synchronized void  printA() throws InterruptedException {
        if (flag != 1){
            this.wait();
        }
        System.out.print(1);
        System.out.print(2);
        System.out.print(3);
        System.out.println(4);
        flag = 2;
        this.notify();
    }
    public synchronized void  printB() throws InterruptedException{
        if (flag != 2){
            this.wait();
        }
        System.out.print(5);
        System.out.print(6);
        System.out.print(7);
        System.out.println(8);

        flag = 1;
        this.notify();
    }
}

结果:

1234
5678
1234
5678
1234
5678
1234
5678
1234
5678
1234
5678
...

三个以上的线程间通信
需要用notifyAll()唤醒所有线程,当某个线程满足条件则执行,其他的就继续休眠,注意if判断要改成while否则判断休眠后被唤醒直接执行下面代码,while循环就可以继续判断。

public class MyClass {
    public static void main(String[] args) {
        final Print print = new Print();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printA();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printB();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printC();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread().start();
    }
}
class Print{
    int flag = 1;
    public synchronized void  printA() throws InterruptedException {
        while (flag != 1){
            this.wait();
        }
        System.out.print(1);
        System.out.print(2);
        System.out.print(3);
        System.out.println(4);
        flag = 2;
        this.notifyAll();
    }
    public synchronized void  printB() throws InterruptedException{
        while (flag != 2){
            this.wait();
        }
        System.out.print(5);
        System.out.print(6);
        System.out.print(7);
        System.out.println(8);
        flag = 3;
        this.notifyAll();
    }
    public synchronized void  printC() throws InterruptedException{
        while (flag != 3){
            this.wait();
        }
        System.out.print(9);
        System.out.print(10);
        System.out.print(11);
        System.out.println(12);
        flag = 1;
        this.notifyAll();
    }
}
1234
5678
9101112
1234
5678
9101112

我们发现上面的做法会把所有的线程都唤醒,这就大大消耗资源,

下面使用ReentranLock
它是个可重入锁
1、可重入锁是指同一个线程可以多次获取同一把锁,当然synchronized也可以
2、可中断锁,即响应中断
3、公平锁和非公平锁,公平锁是指多个线程同时尝试获取同一把锁时,获取锁的顺序按照线程达到的顺序,而非公平锁则允许线程“插队”。
4、互斥锁
synchronized是个关键字,而它是个类
看看结构:
在这里插入图片描述

 int	getHoldCount()
          查询当前线程保持此锁的次数。
protected  Thread	getOwner()
          返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
protected  Collection<Thread>	getQueuedThreads()
          返回一个 collection,它包含可能正等待获取此锁的线程。
 int	getQueueLength()
          返回正等待获取此锁的线程估计数。
protected  Collection<Thread>	getWaitingThreads(Condition condition)
          返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
 int	getWaitQueueLength(Condition condition)
          返回等待与此锁相关的给定条件的线程估计数。
 boolean	hasQueuedThread(Thread thread)
          查询给定线程是否正在等待获取此锁。
 boolean	hasQueuedThreads()
          查询是否有些线程正在等待获取此锁。
 boolean	hasWaiters(Condition condition)
          查询是否有些线程正在等待与此锁有关的给定条件。
 boolean	isFair()
          如果此锁的公平设置为 true,则返回 trueboolean	isHeldByCurrentThread()
          查询当前线程是否保持此锁。
 boolean	isLocked()
          查询此锁是否由任意线程保持。
 void	lock()
          获取锁。
 void	lockInterruptibly()
          如果当前线程未被中断,则获取锁。
 Condition	newCondition()
          返回用来与此 Lock 实例一起使用的 Condition 实例。
 String	toString()
          返回标识此锁及其锁定状态的字符串。
 boolean	tryLock()
          仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
 boolean	tryLock(long timeout, TimeUnit unit)
          如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。
 void	unlock()
          试图释放此锁。

使用方法:

public class MyClass {
    public static void main(String[] args) {
        final Print print = new Print();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printA();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printB();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while(true){
                        print.printC();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread().start();
    }
}
class Print{
    private int flag = 1;
    private ReentrantLock r = new ReentrantLock();
    private Condition condition1 = r.newCondition();
    private Condition condition2 = r.newCondition();
    private Condition condition3 = r.newCondition();
    public void  printA() throws InterruptedException {
        r.lock();//同步
            if (flag!=1){
                condition1.await();
            }
            System.out.print(1);
            System.out.print(2);
            System.out.print(3);
            System.out.println(4);
            flag = 2;
            condition2.signal();
        r.unlock();
    }
    public void  printB() throws InterruptedException{
        r.lock();//同步
            if (flag!=2){
                condition2.await();
            }
            System.out.print(5);
            System.out.print(6);
            System.out.print(7);
            System.out.println(8);
            flag = 3;
            condition3.signal();
        r.unlock();
    }
    public void  printC() throws InterruptedException{
        r.lock();//同步
            if (flag!=3){
                condition3.await();
            }
            System.out.print(9);
            System.out.print(10);
            System.out.print(11);
            System.out.println(12);
            flag = 1;
            condition1.signal();
        r.unlock();
    }
}

结果:

1234
5678
9101112
1234
5678
9101112

tryLock()即使锁已经被持有也会执行代码。并返回false


可以这么用
```java
if (r.tryLock()){
            System.out.print(5);
            System.out.print(6);
            System.out.print(7);
            System.out.println(8);
        }

中断
看看人家怎么实现的
https://www.jianshu.com/p/120872f406f3?from=singlemessage
使用lockInterruptibly()获得锁,如果发生死锁,调用线程interrupt来消除死锁。

ReentrantLock.lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。而ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态。

public class DeadLock3 {

    private static Lock lock1 = new ReentrantLock();
    private static Lock lock2 = new ReentrantLock();

    public static void deathLock() {
        new Thread() {
            @Override
            public void run() {
                try {
                    lock1.lockInterruptibly();
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        lock2.lockInterruptibly();
                        System.out.println("thread 1 ...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock2.unlock();
                    }

                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                } finally {
                    lock1.unlock();
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                try {
                    lock2.lockInterruptibly();
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        lock1.lockInterruptibly();
                        System.out.println("thread 1 ...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock1.unlock();
                    }

                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                } finally {
                    lock2.unlock();
                }
            }
        }.start();
    }

    public static void main(String[] args) throws InterruptedException {
        deathLock();

        TimeUnit.SECONDS.sleep(2);
        checkDeadLock();
    }

    //基于JMX获取线程信息
    public static void checkDeadLock() {
        //获取Thread的MBean
        ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
        //查找发生死锁的线程,返回线程id的数组
        long[] deadLockThreadIds = mbean.findDeadlockedThreads();
        System.out.println("---" + deadLockThreadIds);
        if (deadLockThreadIds != null) {
            //获取发生死锁的线程信息
            ThreadInfo[] threadInfos = mbean.getThreadInfo(deadLockThreadIds);
            //获取JVM中所有的线程信息
            Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
            for (Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
                for (int i = 0; i < threadInfos.length; i++) {
                    Thread t = entry.getKey();
                    if (t.getId() == threadInfos[i].getThreadId()) {
                        //中断发生死锁的线程
                        t.interrupt();
                        //打印堆栈信息       
                        // for (StackTraceElement ste : entry.getValue()) {
                        // // System.err.println("t" + ste.toString().trim());
                        // }
                    }
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值