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
上一篇: actionscript3里的as关键字和强制类型转换
下一篇: Ta们二成这样,不知你笑成那样