JAVA多线程——(一)多线程编程

JAVA多线程——多线程编程

【一】线程创建

  • Thread创建:

如果要使用Thread来创建线程,需要新建一个类来继承Thread,重新run方法

class MyThread extends Thread {
    
    /**
     * 这是线程任务 
     */
    @Override
    public void run() {
        System.out.println("这是新建线程");
    }
}

public class Main {
    public static void main(String args[]){
        //创建线程
       MyThread myThread = new MyThread();
       //执行线程任务
       myThread.start();
    }
}
  • Runnable
    使用runnable需要写一个实现这个接口的类,但是这种方法是没返回值的
class MyTask implements Runnable {

    /**
     * 这是线程任务
     */
    @Override
    public void run() {
        System.out.println("这是新建线程");
    }
}

public class Main {
    public static void main(String args[]) {
        //创建任务
        MyTask task = new MyTask();
        //创建线程
        Thread thread = new Thread(task);
        
        //执行线程任务
        thread.start();
    }
}

【二】线程状态转换

在这里插入图片描述

  • 1、新建状态(New):新创建了一个线程对象。

  • 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。

  • 3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  • 4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态

  • 5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

详细线程状态转换请移步到博客:https://blog.csdn.net/wenge1477/article/details/90481125

【三】守护线程

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

java中线程包括两种:
1、User Thread(用户线程)
2、Daemon Thread(守护线程)

class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("这是新建线程");
    }
    
}

public class Main {
    public static void main(String args[]) {
        //创建任务
        MyTask task = new MyTask();
        //创建线程
        Thread thread = new Thread(task);

        //设置为守护线程
        thread.setDaemon(true);

        //执行线程任务
        thread.start();
    }
}

  • // 设定 daemonThread 为 守护线程,default false(非守护线程)
 daemonThread.setDaemon(true);  
  • // 验证当前线程是否为守护线程,返回 true 则为守护线程
 daemonThread.isDaemon();  

【四】线程同步

即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多,临界区对象就是其中一种

  • synchronied
    synchronied进行对象锁,锁住对象,
  • ReentranLock
  • ReadWriteLock
  • wait和notify
  • condition
  • 阻塞队列
  • CountDownLatch
  • CyclicBarrier

【五】线程死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。各自持有资源不释放,确申请获取对方手中的资源,形成相互等待

  • 互斥条件:一个资源每次只能被一个进程使用。

  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

死锁例子:

public class DeadLockSample extends Thread{
    private String first;
    private String second;
    public DeadLockSample (String name,String first,String second){
        super(name);
        this.first = first;
        this.second = second;
    }

    public void run(){
        synchronized (first){
            System.out.println(this.getName() + " obtained:" + first);

            try {
                Thread.sleep(1000L);
                synchronized (second){
                    System.out.println(this.getName() +" obtained: " + second);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        String lockA = "lockA";
        String lockB = "lockB";
        DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
        DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

    }

}
结果:
Thread1 obtained:lockA
Thread2 obtained:lockB
或者
Thread2 obtained:lockB
Thread1 obtained:lockA


预防死锁的几种方法:

  • 加锁顺序(线程按照一定的顺序加锁)
    如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生

  • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    在申请资源的时候设置时间限制,当时间到了就不在申请资源,比如lock的tryLocak方法可以设置时间

  • 死锁检测

【六】synchronied

synchronized关键字是java并发编程中必不可少的工具,synchronized是围绕一个被称为内部锁或监视锁的内部实体实现的(Api规范里经常将其简单的称之为“monitor”)。内部锁在同步的两个方面发挥作用:强制独占访问对象状态和建立对可见性必不可少的happens-before关系

  • 同步代码块
    锁住的是一个对象
1、第一种
 public void getCunt(){
        synchronized (this){
            System.out.println("这是同步代码块!!!");
        }
}

2、第二种
 public synchronized void getCunt() {
        System.out.println("这是同步代码块!!!");
}
  • 同步方法
    锁住的是当前的类的class
1、第一种
public void getCunt() {
        synchronized (Main.class) {
            System.out.println("这是同步代码块!!!");
        }
}

2、第二种
public static synchronized void getCunt() {
        System.out.println("这是同步代码块!!!");
    }

【七】wait和notify

public class Main {
    public static void main(String args[]){
        String lock = new String();

        Stack<Integer> stack = new Stack<>();
        AtomicInteger i = new AtomicInteger(1);

        //生产者生产数据
        Thread producer = new Thread(() -> {
            synchronized (lock) {
                while (stack.isEmpty()) {
                    stack.push(new Integer(i.getAndIncrement()));
                    lock.notifyAll();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        //消费者消费数据
        Thread consumer = new Thread(() -> {
            synchronized (lock) {
                while (!stack.isEmpty()) {
                    System.out.print(stack.pop()+" ");
                    lock.notifyAll();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        producer.start();
        consumer.start();
    }
}


【八】链接

https://blog.csdn.net/J080624/article/details/82721827
https://www.jianshu.com/p/f2c91afe6266

发布了130 篇原创文章 · 获赞 88 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/wenge1477/article/details/102996845