JAVA多线程小记

以下摘自实战java高并发,后续再补充吧

和操作系统中线程的状态略有不同,java中线程所有的状态是在Thread的State枚举中有定义的;

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

简单点就是

    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

其中这个new状态表示刚刚创建进程,该进程还没有执行;等线程的start()方法调用,才表示线程开始执行,线程执行时处于RUNNABLE状态,表示线程所需一切资源都已经准备好了;若执行过程中遇到了synchronized同步块,就会进入BLOCKED阻塞状态,这时线程暂停,直到获得请求的锁。WAITING和TIMED_WAITING状态都表示等待,但是waiting是一个无时间限制的状态,通常都是等待一些特殊事件,比如说wait()方法等待的进程在等待notify方法来唤醒,而通过join()方法等待的线程则会等待目标线程的终止,一旦等到了期望的时间,线程就会再次执行,进入RUNNABLE 状态,当线程执行完毕,则进入了TERMINATED状态表示结束;

1.新建线程

Thread t1 = new Thread()
t1.start()

start()方法负责新建一个线程并且让这个线程执行run()方法,虽然直接t1.run()也可以执行,但是却不能建立一个新线程,而是在当前线程中调用run()方法,这就只是一个普通的方法了;

Thread中默认run()方法什么也没做,因此你可以在新建线程时重载构造方法块中的run()方法,或者继承Thread类,或实现RUNNABLE接口;

	public class testtre {
		private static Thread t1 = new Thread(){
			@Override
		    public void run() {
				System.out.println("HELLO");
			}
		};
		public static void main(String args[]) {
			t1.start();
		}
}

而Runnable接口本身是一个单方法接口,只有run()方法

public interface Runnable(){
    public abstract void run();
}

Thread类中有个重要构造方法

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

这个玩意的形参是RUNNABLE接口的一个实例,在start()方法调用时,新的线程会执行Runnable.run()方法,实际上,Thread中run()方法的源码如下:

public
class Thread implements Runnable{    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
/*
..
..
*/
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

看到没,Thread.run()就是直接调用内部的Runnable接口,所以使用Runnable接口告诉线程它该做什么:

public class CreateThread3 implements Runnable{
	public static void main(String args[]) {
	Thread t1 = new Thread(new CreateThread3());
	t1.start();
	}
	@Override
	public void run() {
		System.out.println("I am Runnable");
	}
}

如上所示,我们用一个CreateThread3实现了Runnable接口,然后在main()方法中新建一个CreateThread3实例作为Thread类的参数,当Thread类调用其构造方法public Thread(Runnable target)实例化一个对象时,就会新建一个t1进程,新的进程会调用Runnable.run()方法;

###########################################################################################

2.线程中断

严格来讲,线程中断不会使线程立即退出,而是给线程发送一个通知,有人叫你滚蛋啦~,至于线程接到通知后理不理你,完全是看目标线程自己决定,这个很重要;

线程中断有关的三个方法:

public void Thread.interrupt()
public void Thread.isInterrupted()
public static boolean Thread.interrupted()

Thread.interrupt()方法是一个实例方法,它通知目标线程中断,也就是设置中断标志位,这个东西表示当前县城已经被中断了;

Thread.isInterrupted()也是一个实例方法,通过检查中断标志位来判断当前线程是否有被中断;

Thread.interrupted()也是一个实例方法用来判断当前线程的中断状态,但是会清除当前线程的中断标志位状态;

例如希望t1在中断后退出,就加一个中断处理代码:

public class CreateThread3 {
	public static void main(String args[]) throws InterruptedException {
		Thread t1 = new Thread() {
			@Override
			public void run() {
				while(true) {
					if(Thread.currentThread().isInterrupted()) {
						System.out.println("Interrupted");
						break;
					}
					Thread.yield();
				}
			}
		};
		t1.start();
		Thread.sleep(1000);
		t1.interrupt();
	}

}

要注意的是,只要有Thread.sleep()方法,就要抛出InterruptedException,这玩意不是运行时异常,就必须在程序中捕获并且处理它;

2.等待(wait)和通知(notify)

这两个方法是在Object类中的,也就是说,任何对象都可以调用这两个方法;

    public final void wait() throws InterruptedException {
        wait(0);
    }
    
    public final native void notify();

当一个实例对象调用了wait()方法后,当前线程会在这个对象上等待,比如线程A中调用了obj.wait()方法,A就会停止继续执行,变为等待状态,一直到有其他线程调用了obj.notify()方法为止;若是一个线程调用了object.wait()方法,那么它就会进入到object对象的等待队列,这个等待队列中,可能有多个线程,因为系统运行多个线程同时等待一个对象,当obj.notify()被调用,它会从等待队列中随机选取一个线程唤醒,这个过程完全随机;类似的notifyAll方法会唤醒这个等待队列中所有等待的线程;

!!!!!!!!Object.wait()方法不是随便调用的额,它必须包含在对应的synchronized语句中,无论是wait()还是notify()都需要获得对象的一个监视器;

3.等待线程结束(join)和谦让(yield)

适用于一个线程的输入依赖于另外一个或者多个线程的输出,这个线程要等待依赖线程执行完毕,才能继续

    public final void join() throws InterruptedException {
        join(0);
    }
    public final synchronized void join(long millis) throws InterruptedException

第一个join()方法表示无限等待,会一直阻塞当前线程,直到目标线程执行完毕,第二个会给出一个最大等待时间,若超过给定时间目标线程还在执行,当前线程就等不及了,继续执行;

public class JoinMain {
	public volatile static  int i = 0;
	public static class AddThread extends Thread{
		@Override
		public void run() {
			for(i = 0;i<10000;i++);
		}
		
	}
	public static void main(String args[]) throws InterruptedException{
		AddThread at= new AddThread();
		at.start();
		at.join();
		System.out.println(i);
		
	}

}

在这里,主函数中用了join()等待线程AddThread,不然很可能线程AddThread还没有开始执行,i的值就已经输出了,但是使用 了join()方法后,主线程愿意等待AddThread执行完毕;

另一个Thread.yield()方法,它的定义:

public static native void yield();

一个使当前线程让出cpu的静态方法,让出cpu不是说当前线程不执行了,当前线程让出cpu后还会进行cpu的资源争夺,能否再次分配到却不一定,使用场景:一个不是很重要或者优先级很低的线程,你又害怕他会占用太多的cpu资源,这时就可以用yield方法;

3. volatile和JMM

volatile(易变的,不稳定的),用这个关键字声明一个变量,就等于告诉了虚拟机,这个变量极有可能会被某些程序或者线程修改,但是他不能替代锁,也无法保证复合操作的原子性。

public class PlusTask implements Runnable{
	static volatile int i = 0;
	@Override
	public void run() {
		for(int k = 0;k<10000;k++) {
			i++;
		}
	}
   public static void main(String args[]) throws InterruptedException{
	   Thread[] threads = new Thread[10];
	   for(int i =0;i<10;i++) {
		   threads[i] = new Thread(new PlusTask());
		   threads[i].start();
	   }
	   for(int i =0;i<10;i++) {
		   threads[i].join();
	   }
	   System.out.println(i);
	   
   }

}

这段代码中的i++不是原子性的,所以输出总会小于100000;

那么如何保证线程安全呢,我们对上述代码做一些小改动

public class PlusTask implements Runnable{
	static PlusTask instance = new PlusTask();
	static int i = 0;
	public synchronized void increase() {
		i++;
	}
	@Override
	public void run() {
		for(int k = 0;k<10000;k++) {
			increase();
		}
	}
   public static void main(String args[]) throws InterruptedException{
	   Thread[] threads = new Thread[10];
	   for(int i =0;i<10;i++) {
		   threads[i] = new Thread(instance);
		   threads[i].start();
	   }
	   for(int i =0;i<10;i++) {
		   threads[i].join();
	   }
	   System.out.println(i);
	   
   }

}

这段代码引入了同步关键字synchronized,为了避免两个线程同时对i进行写入,其中一个线程的结果会覆盖另外一个;

关键字synchronized的工作是对同步的代码加锁,使得每一次,只能有一个进程进入同步的代码块,从而保证线程间的安全性;

关键字synchronized可以有多种用法:

1.指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁;

public class AccountSync implements Runnable{
	static AccountSync instance = new AccountSync();
	static int i = 0;
	@Override
	public void run() {
		for(int j = 0;j<1000000;j++) {
			synchronized(instance) {
				i++;
			}
		}
		
	}
	public static void main(String args[]) throws InterruptedException{
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
	
}

2.直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁;

public class AccountingVol implements Runnable{
    static AccountingVol instance=new AccountingVol();
	static volatile int i = 0;
	public synchronized void  increase() {
		i++;
	}
	@Override
	public void run() {
		for(int j = 0;j< 1000000;j++) {
			increase();
		}
	}
	public static void main(String[] args) throws Exception{
		
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

细心点可以发现,这里使用的Runnable接口创建的两个线程,都指向了同一个Runnable接口实例(instance对象),这样才能保证两个线程工作时能够关注到同一个对象锁上,保证线程安全,若是不想这样,可以采用第三种方法;

3.直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁

public class AccountingVol implements Runnable{
   
	static volatile int i = 0;
	public static synchronized void  increase() {
		i++;
	}
	@Override
	public void run() {
		for(int j = 0;j< 1000000;j++) {
			increase();
		}
	}
	public static void main(String[] args) throws Exception{
		
		Thread t1 = new Thread(new AccountingVol());
		Thread t2 = new Thread(new AccountingVol());
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

这样的话虽然两个线程指向了不同的Runnable对象,但是由于方法块需要请求的是当前类的锁,而非当前实例,线程之间还是可以正确的同步;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值