1.使用阻塞队列(BlockingQueue)控制线程通信
package com.threadqueue.test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockQueue {
public static void main(String[] args) throws InterruptedException {
//定义一个长度为2的阻塞队列
BlockingQueue<String> bq=new ArrayBlockingQueue<>(2);
bq.put("Java");//与bq.add("Java"),bq.offer("Java")相同;
bq.put("Java");//与bq.add("Java"),bq.offer("Java")相同;
bq.put("Java");//阻塞线程
}
}
package com.threadqueue.test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
//利用BlockQueue来实现进程间的通信
class Producer extends Thread
{
private BlockingQueue<String> bq;
public Producer(BlockingQueue<String> bq)
{
this.bq=bq;
}
public void run()
{
String[] strArr=new String[]
{
"Java","Python","Spring"
};
for(int i=0;i<6;i++)
{
System.out.println(getName()+"生产者准备生产集合元素!");
try{
Thread.sleep(200);
//尝试放入元素,如果队列已满,则线程被阻塞
bq.put(strArr[i%3]);
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println(getName()+"生产完成:"+bq);
}
}
}
class Consumer extends Thread
{
private BlockingQueue<String> bq;
public Consumer(BlockingQueue<String> bq)
{
this.bq=bq;
}
public void run()
{
while(true)
{
System.out.println(getName()+"消费者准备消费集合元素!");
try{
Thread.sleep(200);
//尝试取出元素,如果队列为空,则线程阻塞
bq.take();
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.println(getName()+"消费完成:"+bq);
}
}
}
public class BlockQueueTest {
public static void main(String[] args) {
//创建一个容量为1的BlockingQueue
BlockingQueue<String> bq=new ArrayBlockingQueue<>(1);
//启动3个生产线程
new Producer(bq).start();
new Producer(bq).start();
new Producer(bq).start();
//启动另一个消费者线程
new Consumer(bq).start();
}
}
3.线程和未处理的异常
package com.threadqueue.test;
class MyThread extends Thread{
//提供指定线程名的构造器
public MyThread(String name)
{
super(name);
}
//提供指定线程名、线程组的构造器
public MyThread(ThreadGroup group,String name)
{
super(group,name);
}
public void run()
{
for(int i=0;i<5;i++)
{
System.out.println(getName()+"i的变量"+i);
}
}
}
public class ThreadGroupTest {
public static void main(String[] args) {
//获取主线程所在的线程组,这是所有线程默认的线程组
ThreadGroup mainGroup=Thread.currentThread().getThreadGroup();
System.out.println("主线程组的名字:"+mainGroup.getName());
System.out.println("主线程组是否是后台线程:"+mainGroup.isDaemon());
new MyThread("主线程组的线程").start();
ThreadGroup tg=new ThreadGroup("新线程");
tg.setDaemon(true);
System.out.println("tg线程是否是后台线程:"+tg.isDaemon());
MyThread tt=new MyThread(tg,"tg组的线程甲");
tt.start();
new MyThread(tg,"tg线程组的线程乙").start();
}
}
package com.threadqueue.test;
//定义自己的异常处理器
class MyExHandler implements Thread.UncaughtExceptionHandler
{
//实现UncaughtException()方法,该方法将处理线程的未处理异常
public void uncaughtException(Thread t,Throwable e)
{
System.out.println(t+"线程出现了异常:"+e);
}
}
public class ExThreadHandler {
public static void main(String[] args) {
//设置主线程的异常处理器
Thread.currentThread().
setUncaughtExceptionHandler(new MyExHandler());
int a=2/0;
System.out.println("程序正常结束!");
}
}
4.线程池
package com.threadpool.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThrashPool {
public static void main(String[] args) {
//创建一个具有固定线程数(6)的线程池
ExecutorService pool=Executors.newFixedThreadPool(6);
//使用Lambda表达式创建Runnable对象
Runnable target=()->{
for(int i=0;i<5;i++)
{
System.out.println(Thread.currentThread().getName()+
"的i值为:"+i);
}
};
//向线程池中提交两个线程
pool.submit(target);
pool.submit(target);
//关闭线程池
pool.shutdown();
}
}
package com.threadpool.test;
import java.util.List;
import java.util.concurrent.*;
//继承RecursiveAction来实现“可分解”的任务
class PrintTask extends RecursiveAction
{
//每个小任务最多只打印20个数
private static final int ts=10;
private int start;
private int end;
//打印从start到end的任务
public PrintTask(int start,int end)
{
this.start=start;
this.end=end;
}
protected void compute()
{
//当end与start之间的差小于ThresHold时,开始打印
if(end-start<ts)
{
for(int i=start;i<end;i++)
{
System.out.println(Thread.currentThread().getName()+
"的i值:"+i);
}
}
else
{
//当end与start之间的差大于ThresHold时,即要打印的数超过20
//将大任务分解成两个“小任务”
int middle=(start+end)/2;
PrintTask left=new PrintTask(start,middle);
PrintTask right=new PrintTask(middle,end);
//并行执行两个小任务
left.fork();
right.fork();
}
}
}
public class ForkJoinPoolTest {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool pool=new ForkJoinPool();
//提交可分解的PrinTask任务
pool.submit(new PrintTask(0,20));
pool.awaitTermination(2,TimeUnit.SECONDS);
//关闭线程池
pool.shutdown();
}
}
package com.threadpool.test;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
//继承RecursiveTask来实现可分解任务
class CalTask extends RecursiveTask<Integer>
{
//每个小任务最多只累加10个数
private static final int ts=10;
private int arr[];
private int start;
private int end;
//累加从start到end的数组元素
public CalTask(int[] arr,int start,int end)
{
this.arr=arr;
this.start=start;
this.end=end;
}
protected Integer compute()
{
int sum=0;
//当end与start之间的差小于ts时,开始进行累加
if(end-start<ts)
{
for(int i=start;i<end;i++)
{
sum+=arr[i];
}
return sum;
}
else
{
//当end与start之间的差大于ts时,即要累加的数超过10个时
//将大人物分解成两个小任务
int middle=(start+end)/2;
CalTask left=new CalTask(arr,start,middle);
CalTask right=new CalTask(arr,middle,end);
//并行执行两个小任务
left.fork();
right.fork();
//把两个小任务累加的结果合并起来
return (left.join()+right.join());
}
}
}
public class FJPSum {
public static void main(String[] args)
throws InterruptedException, ExecutionException {
int[] arr=new int[100];
Random rand=new Random();
int total=0;
//初始化100个数字元素
for(int i=0,len=arr.length;i<len;i++)
{
int tmp=rand.nextInt(30);
//对数组元素赋值,并将数组元素添加到sum总和中
total+=(arr[i]=tmp);
}
System.out.println(total);
//创建一个通用池
ForkJoinPool pool=ForkJoinPool.commonPool();
//提交可分解的CalTask任务
Future<Integer> future=pool.submit(
new CalTask(arr,0,arr.length));
System.out.println(future.get());
//关闭线程池
pool.shutdown();
}
}
5.线程相关类
package com.threadlocal.test;
class Account{
/*
* 定义一个ThreadLocal类型的变量,改变量是一个线程局部变量
* 每个线程都会保留该变量的构造器
*/
private ThreadLocal<String> name=new ThreadLocal<>();
//定义一个初始化name成员变量的构造器
public Account(String str)
{
this.name.set(str);
//下面代码用于访问当前线程的name副本的值
System.out.println("----"+this.name.get());
}
public String getName() {
return name.get();
}
public void setName(String str) {
this.name.set(str);
}
}
class MyTest extends Thread
{
//定义一个Account类型的成员变量
private Account account;
public MyTest(Account account,String name)
{
super(name);
this.account=account;
}
public void run()
{
//循环十次
for(int i=0;i<10;i++)
{
//当i==6时输出将账户名替换成当前线程名
if(i==6)
{
account.setName(getName());
}
//输出同一个账户的账户名和循环变量
System.out.println(account.getName()+
"账户的i值:"+i);
}
}
}
public class ThreadLocalTest {
public static void main(String[] args) {
//启动两个线程,两个线程共享同一个Account
Account at=new Account("初始名");
/*
* 虽然两个线程共享同一个账户,即只有一个账户名
* 但由于账户名是ThreadLocal类型的,所以每个线程都完全拥有各自的账户名副本,
* 因此在i==6之后,将看到两个线程访问同一个账户时出现不同的账户名
*/
new MyTest(at,"线程甲").start();
new MyTest(at,"线程乙").start();
}
}