并发编程简介
并发编程可以帮助我们将程序划分为多个分离的、独立运行的任务。通过多线程机制,这些独立任务中的每一个都将由执行线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,因此单个进程可以拥有多个并发执行的任务,但是程序使得每个任务都好像拥有自己的CPU一样,其底层机制是切分CPU时间。CPU会轮流为每个任务分配占用时间。
线程共享静态变量
每个线程拥有各自独立的地址空间,而其中的静态成员变量是可以被多个线程共享的,例如下面程序中的i,i的地址是唯一的,并且永远存放在静态区域,因此,每次对i进行加载读取写入都是有可能发生不正当竞争的,为了保证读写安全,特别对i进行加锁synchronized,这样保证了安全读写。
package concurrency;
/**
* 并发问题,多线程的静态变量可以共享i
* @author zzw922cn
*
*/
public class CountRunnable implements Runnable {
private static Integer i=0;
@Override
public void run() {
synchronized(i) {
System.out.println(Thread.currentThread().getName()+":"+(++i));
}
}
}
package concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test1 {
public static void main(String[] args) {
//Executors.newCachedThreadPool()用来新建线程池,如果有空闲线程就使用它,否则就新建新线程
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Thread[] thread = new Thread[10];
for(int i=0;i<thread.length;i++) {
thread[i]=new Thread(new CountRunnable());
newCachedThreadPool.submit(thread[i]);
}
newCachedThreadPool.shutdown();
}
}
pool-1-thread-2:2
pool-1-thread-4:4
pool-1-thread-3:3
pool-1-thread-1:1
pool-1-thread-5:5
pool-1-thread-6:6
pool-1-thread-7:7
pool-1-thread-8:8
pool-1-thread-10:9
pool-1-thread-9:10
从任务中返回值(Callable接口)
有时候,我们在分割任务时,同时还希望每个任务执行完毕能够返回一些数据给主进程,这时候我们可以使用实现Callable接口的方式来实现。Callable是个泛型接口,其中必须要实现的方法是Call()方法,该方法的返回值即该任务向主线程提交的返回值,由于是泛型的,因此我们可以返回任意类型的对象。
下面,我们来进行多个求和任务的分割,代码如下:
package concurrency;
import java.util.concurrent.Callable;
public class CountNumberTask implements Callable<Integer> {
private int num;
/**
* 构造函数
* @param num 末尾数
*/
public CountNumberTask(int num) {
this.num = num;
}
/**
* 实现call函数,返回结果
*/
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=1;i<=num;i++) {
sum+=i;
}
return sum;
}
}
package concurrency;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 耗时26秒
* @author zzw922cn
*
*/
public class CountNumberTest {
public static void main(String[] args) {
long t1 = System.currentTimeMillis();
ExecutorService newCachedThreadPool = Executors.newFixedThreadPool(2);
for(int i=1;i<=300000;i++) {
Future<Integer> submit = newCachedThreadPool.submit(new CountNumberTask(i));
try {
Integer integer = submit.get();
System.out.println(integer);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
newCachedThreadPool.shutdown();
long t2 = System.currentTimeMillis();
System.out.println("多线程耗时"+(t2-t1)/1000.0+"s");
}
}
package concurrency;
/**
* 耗时37秒
* @author zzw922cn
*
*/
public class CountNumberTest2 {
public static void main(String[] args) {
long t1 = System.currentTimeMillis();
for(int i=1;i<=300000;i++) {
int sum=0;
for(int j=0;j<=i;j++) {
sum+=j;
}
System.out.println(sum);
}
long t2 = System.currentTimeMillis();
System.out.println("单线程耗时"+(t2-t1)/1000.0+"s");
}
}
守护线程
所谓守护线程(又称作后台线程),是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的一部分。当所有非守护进程结束时,程序就终止了,同时也会杀死程序的所有守护线程,守护线程在被杀死时,不会执行finally语句块。
package concurrency;
public class DeamonRunnable implements Runnable {
@Override
public void run() {
try {
while(true) {
Thread.sleep(100);
System.out.println(Thread.currentThread()+" "+this);
}
} catch(InterruptedException ex) {
System.out.println("Sleep() interrupted!");
}
}
}
package concurrency;
import java.util.concurrent.TimeUnit;
public class DeamonRunnableTest {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++) {
Thread thread = new Thread(new DeamonRunnable());
thread.setDaemon(true);
thread.start();
}
//设置主线程休眠,所以能看到守护线程
TimeUnit.MILLISECONDS.sleep(175);
}
}
Thread[Thread-1,5,main] concurrency.DeamonRunnable@4908d73b
Thread[Thread-6,5,main] concurrency.DeamonRunnable@3f91c80b
Thread[Thread-5,5,main] concurrency.DeamonRunnable@1fb44aeb
Thread[Thread-0,5,main] concurrency.DeamonRunnable@12d4f26
Thread[Thread-2,5,main] concurrency.DeamonRunnable@210c4256
Thread[Thread-4,5,main] concurrency.DeamonRunnable@65c16078
Thread[Thread-3,5,main] concurrency.DeamonRunnable@7dca373f
Thread[Thread-9,5,main] concurrency.DeamonRunnable@7105d5e
Thread[Thread-8,5,main] concurrency.DeamonRunnable@7995373b
Thread[Thread-7,5,main] concurrency.DeamonRunnable@6bad1bc1
守护线程的子线程
通过下面的代码运行结果我们会发现,对于守护线程而言,它的所有子线程也都是守护线程。
package concurrency;
public class Daemon implements Runnable {
private Thread[] t=new Thread[10];
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<t.length;i++) {
t[i]=new Thread(new DaemonSpawn());
t[i].start();
System.out.println(t[i]+"has started!");
}
for(int i=0;i<t.length;i++) {
System.out.println(t[i]+"is DaemonThread:"+t[i].isDaemon());
}
while(true) {
Thread.yield();
}
}
}
package concurrency;
public class DaemonSpawn implements Runnable {
@Override
public void run() {
while(true) {
//让步
Thread.yield();
}
}
}
package concurrency;
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Daemon());
thread.setDaemon(true);
thread.start();
System.out.println(thread+"isDaemonThread:"+thread.isDaemon());
Thread.sleep(100);
}
}
Thread[Thread-0,5,main]isDaemonThread:true
Thread[Thread-1,5,main]has started!
Thread[Thread-2,5,main]has started!
Thread[Thread-3,5,main]has started!
Thread[Thread-4,5,main]has started!
Thread[Thread-5,5,main]has started!
Thread[Thread-6,5,main]has started!
Thread[Thread-7,5,main]has started!
Thread[Thread-8,5,main]has started!
Thread[Thread-9,5,main]has started!
Thread[Thread-10,5,main]has started!
Thread[Thread-1,5,main]is DaemonThread:true
Thread[Thread-2,5,main]is DaemonThread:true
Thread[Thread-3,5,main]is DaemonThread:true
Thread[Thread-4,5,main]is DaemonThread:true
Thread[Thread-5,5,main]is DaemonThread:true
Thread[Thread-6,5,main]is DaemonThread:true
Thread[Thread-7,5,main]is DaemonThread:true
Thread[Thread-8,5,main]is DaemonThread:true
Thread[Thread-9,5,main]is DaemonThread:true
Thread[Thread-10,5,main]is DaemonThread:true