Java必知必会--多线程解析基础篇

2019-03-01 21:36:42  卢浮宫  版权声明:本文为站长原创文章,转载请写明出处


一、多线程是实现(这里介绍常用的两个)

        1、实现Runnable接口,重写run方法,代码如下:
            public class XaThread implements Runnable {
public static void main(String[] args) throws InterruptedException {
XaThread xaThread = new XaThread();
Thread mThread = new Thread(xaThread);
mThread.start();
}

@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}


        2、继承Thread类,代码如下:              
            public class Xthread{
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}

class MyThread extends Thread{
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


        3、两者对比:

             ① 继承Thread类后就是单继承了,会隔绝来自其他类的继承,而实现Runnable接口则消除了这种不良。

             ② class Thread implements Runnable {...}可以看到继承了Thread类后还是要实现Runnable接口,

                会产生多个Runnable示例对象。而实现Runnable则不会,可以多线程处理同一资源,使用一个Runnable示例产生多个线程,且可以资源共享。


二、线程的5种状态以及切换

      ①新建:新建了一个线程(继承Thread类或实现Runnable接口)

      ②可运行:线程创建后且调用了start方法,则进入线程池,等待被线程调度选用,获取cpu的使用权

      ③运行:可执行的线程获取到了cpu资源,并执行程序代码

      ④阻塞:线程因为某种原因放弃了cpu使用权,暂停了操作,直到转换成可运行状态才有机会继续进行操作。

      ⑤死亡:线程run()main()方法执行完成,或者异常抛出后,线程结束,且不可恢复。


三、阻塞状态

    1、当前线程运行Thread.sleep(1000);会使当前线程休眠1s,进入阻塞状态
    2、运行在当前线程(Thread1)的其他线程(Thread2)调用join方法时,会把Thread2加入执行,Thread1阻塞
    3、等待用户输入时(执行IO操作)当前线程进入阻塞状态
    4、使用Thread.sleep(1000);当前线程阻塞,但不释放对象锁
    5、Thread.join();当前线程进入阻塞,但不释放对象锁,等待Thread完成后继续运行
    6、Object.wait();线程进入阻塞,同时释放对象锁,进入等待列队。使用notify()、或notifyAll()可以唤醒
    7、notify()是唤醒一个任意线程,notifyAll()是唤醒所有等待的线程


四、锁--乐观锁和悲观锁

       1、锁是只有在同步中才有的概念。

       2、乐观锁和悲观锁不单单指锁的类型,事实上是指怎么对待数据并发同步

       3、悲观锁认为数据被拿走就会被修改,所以在每次拿数据时都会上锁。别人在拿这个数据时就会阻塞,知道它获取到锁

       4、乐观锁则认为数据被拿走后不会被修改,所以不会上锁。但是要注意:需要判断期间数据有没有被修改过。

       5、java中的synchronized就是实现的悲观锁,而乐观锁则适用与读操作比较频繁情况(可以提高吞吐量)

          1、死锁的原因及排查方案

              死锁的本质在于对资源的不良竞争。当多线程同时被阻塞,他们中的一个或全部都在等待一个资源被释放,但是这个资源又被其他线程锁定,

           2、死锁的产生条件:

              ①互斥占用:一个资源被占用时其他不能使用。②不可抢占:只能等待资源被释放。③请求和保持:请求其他资源时保持对原有资源占用。

              ④循环:1占用2,2占用3,3占用4,4占用1(花盆上的毛毛虫)


五、线程同步

    1、不可避免会有使用同步的情况,关于线程同步有以下方案:

        ①在同步方法中假如synchronize关键字

        ②使用synchronize快对代码段进行同步处理 

         

六、经典的生产消费逻辑代码                


    public class ProAndCon {
//定义最大容量
private int maxNum = 2;
//产品储存
private LinkedList<Integer> list = new LinkedList<>();

class Producer implements Runnable{

@Override
public void run() {
synchronized(list){
while(list.size() == maxNum){
System.out.println("仓库已满,停止生产");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(1);
System.out.println("生产中,数量为--" + list.size());
list.notify();
}
}
}

class Customer implements Runnable{

@Override
public void run() {
synchronized(list){
while(list.size() == 0){
System.out.println("没有库存,停止消费");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.removeFirst();
System.out.println("消费量一个,当前库存为--" + list.size());
list.notify();
}
}
}

public static void main(String[] args) {
ProAndCon proAndCon = new ProAndCon();
Producer producer = proAndCon.new Producer();
Customer customer = proAndCon.new Customer();
for (int i = 0; i < 10; i++) {
Thread proThread = new Thread(producer);
proThread.start();
Thread comThread = new Thread(customer);
comThread.start();
}
}
}


输出结果如下:

    生产中,数量为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    生产中,数量为--2
    消费量一个,当前库存为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    生产中,数量为--2
    仓库已满,停止生产
    消费量一个,当前库存为--1
    消费量一个,当前库存为--0
    生产中,数量为--1
    消费量一个,当前库存为--0


            



更多精彩请关注guangmuhua.com


最新评论:

2019?3?2?17:53:33
2019-03-02 17:53:35
1楼