线程
1-1 创建一个线程,继承 Thread,重写run方法
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(getName()+i);
}
}
}
1-2 测试类
public class test2 {
public static void main(String[] args) {
MyThread t1 = new MyThread("线程1");
for (int i = 0; i < 100; i++) {
System.out.println("线程2"+i);
}
t1.start();
}
}
2-1 编写类,实现Runnable接口
public class MyThread implements Runnable{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
测试
public class test2 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread, "线程1");
t1.start();
for (int i = 0; i < 100; i++) {
System.out.println("helloworld");
}
}
}
编写类,实现Runnable接口
public class MyThread implements Runnable{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class test2 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
MyThread myThread = new MyThread();
executorService.submit(myThread);
executorService.submit(myThread);
executorService.submit(myThread);
}
}
说了多线程,那就不得不说说多线程的sleep()、join()和yield()三个方法的区别啦
1、sleep()方法
/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * 意思是说:当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 该线程不会失去任何监视器的所有权。 * @param millis * the length of time to sleep in milliseconds * 毫秒为单位 * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;
其实主要的就是他是让其他线程走,自己进行休眠,但是自己却不会释放对象锁,也就是说,如果有同步锁的时候,其他线程不能访问共享数据。
注意该方法要捕获异常 比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一 个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级 的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。 总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的 线程有执行的机会。
2、yield() 方法
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * 意思是说 提示当前线程可以让处理器忽略当前线程,去处理其他线程 *Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * 它是一种启发式尝试,用于改善线程之间的相对进展,否则会过度利用CPU。 它的使用应与详细的分析和基准测试相结合,以确保它实际上具有所需的效果。 *
It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. * 使用这种方法很少是合适的。 它可能对调试或测试目的很有用,它可能有助于重现因竞争条件而产生的错误。 在设计并发控制结构(如中的那些)时,它也可能很有用 */ public static native void yield();
yield() 这个方法从以上注释可以看出,也是一个休眠自身线程的方法,同样不会释放自身锁的标识,区别在于它是没有参数的,即yield()方 法只是使当前线程重新回到可执行状态,
所以执行yield()的线程有可能在进入到可执行状态 后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也 和sleep()方法不同。
3、join() 方法
这个方法比较有意思,Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前, B不能工作。
/** * Waits for this thread to die. * 等待线程死亡 *An invocation of this method behaves in exactly the same * way as the invocation * *
* {@linkplain #join(long) join}{@code (0)} ** * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ public final void join() throws InterruptedException { join(0); // 调用了有参方法 }
/** * Waits at most {@code millis} milliseconds for this thread to * die. A timeout of {@code 0} means to wait forever. * 这个线程最多等多少毫秒,如果超时了,就会进行线程死锁 *This implementation uses a loop of {@code this.wait} calls * conditioned on {@code this.isAlive}. As a thread terminates the * {@code this.notifyAll} method is invoked. It is recommended that * applications not use {@code wait}, {@code notify}, or * {@code notifyAll} on {@code Thread} instances. * * @param millis * the time to wait in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ public final synchronized void join(long millis) throws InterruptedException {
保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。
CountDownLatch在多线程并发编程中充当一个计时器的功能,并且维护一个count的变量,并且其操作都是原子操作,该类主要通过countDown()和await()两个方法实现功能的,首先通过建立CountDownLatch对象,并且传入参数即为count初始值。如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。如果一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操作。比如下面的例子就是有两个操作,一个是读操作一个是写操作,现在规定必须进行完写操作才能进行读操作。所以当最开始调用读操作时,需要用await()方法使其阻塞,当写操作结束时,则需要使count等于0。因此count的初始值可以定为写操作的记录数,这样便可以使得进行完写操作,然后进行读操作。
首先是创建实例 CountDownLatch countDown = new CountDownLatch(2)
需要同步的线程执行完之后,计数-1; countDown.countDown()
需要等待其他线程执行完毕之后,再运行的线程,调用 countDown.await()实现阻塞同步
实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用CyclicBarrier的await方法时,将剩余拦截的线程数减1,然后判断剩余拦截数是否为0,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁,接着先从await方法返回,再从CyclicBarrier的await方法中返回。
CyclicBarrier主要用于一组线程之间的相互等待,而CountDownLatch一般用于一组线程等待另一组些线程。实际上可以通过CountDownLatch的countDown()和await()来实现CyclicBarrier的功能。即 CountDownLatch中的countDown()+await() = CyclicBarrier中的await()。注意:在一个线程中先调用countDown(),然后调用await()。
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理的使用公共资源。
线程可以通过acquire()方法来获取信号量的许可,当信号量中没有可用的许可的时候,线程阻塞,直到有可用的许可为止。线程可以通过release()方法释放它持有
的信号量的许可。
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。因此使用Exchanger的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的。
Exchanger类提供了两个方法,String exchange(V x):用于交换,启动交换并等待另一个线程调用exchange;String exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange,并且设置最大等待时间,当等待时间超过timeout便停止等待。