道理以后会懂,先写写法吧。
任务
线程的创建
创建的基本用法
//创建线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
});//新建线程
//lambda
Thread t2 = new Thread(()->System.out.println("hello world!"));
//t1.run(); 此方法不会创建新线程 在主线程执行
t1.start();//此方法创建新线程,在新线程中进行
t2.start();
不能直接调用t1.run()方法,必须采用start()才能创建
接口或继承
class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println( Thread.currentThread().getName()+ " " + i);
}
}
class MyThread implements Runnable{
@Override
public void run() {
super.run();
System.out.println( Thread.currentThread().getName()+ " " + i);
}
}
线程阻塞
线程运行到该语句时会停止执行一段时间 Thread.sleep(1000);阻塞一秒,或者 TimeUnit.MILLISECONDS.sleep(1000);线程让步
yield();表示该线程已经执行了一些操作,可以转到其他线程了Executor
管理Thread对象,简化并发编程,先看简单用法import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//Executor 执行器 简化了并发编程
public class LiftOff implements Runnable{
protected int downCount = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff(){}
public LiftOff(int count){
downCount = count;
}
public String status(){
return "#" + id + "(" +(downCount>0?downCount:"LiftOff!") + ")";
}
@Override
public void run() {
while(downCount-->0){
System.out.println(status());
try{
//Thread.sleep(1000);//线程休眠 阻塞 单位毫秒 1000毫秒 及一秒
//现在的写法
TimeUnit.MILLISECONDS.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
//Thread.yield();//可以换其他线程 让步,但是结果并不确定,不能依赖
}
public static void main(String...args){
LiftOff l= new LiftOff();
//l.run();
// for(int i = 0;i<5;i++)
// 协作
// new Thread(new LiftOff()).start();
System.out.println("-------");
//以上代码可以执行,以下是对Executor 执行器的简单介绍
ExecutorService exec =
//下面是几种线程池的简单介绍
//事先建好需要的线程 速度快
Executors.newFixedThreadPool(5);//事先创建5个线程准备
//执行过程中创建需要的线程
//Executors.newCachedThreadPool();
//相当于个数为1的FixedThreadPool 需要排队
//Executors.newSingleThreadExecutor();
for(int i =0;i<5;i++)
exec.execute(new LiftOff());//提交任务
exec.shutdown();//之后不可以提交新的任务 程序在执行完所有的任务后退出
}
}
Executor的异常处理
try-catch块不能捕获到异常,如下:import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//利用ExecutorService捕获异常
//try-catch捕获不到
public class ExceptionThread implements Runnable{
@Override
public void run() {
throw new RuntimeException();
}
public static void main(String...args){
ExecutorService ece=null;
//ok
try{
ExceptionThread t = new ExceptionThread();
t.run();
}catch (Exception e){
System.out.println("catch");
}
//wrong
try{
ece = Executors.newCachedThreadPool();
ece.execute(new ExceptionThread());
}catch (Exception e){
System.out.println("catch");
}finally {
if(ece!=null)
ece.shutdown();
}
}
}
Exception in thread “pool-1-thread-1” java.lang.RuntimeException
at com.concurrency.ExceptionThread2.run(CaptureUncaughtException.java:28)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:835)
要对Executor进行异常处理,采用Thread.UncaughtExceptionHandler接口
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
//Executor处理异常的方式
public class CaptureUncaughtException {
public static void main(String...args){
//两种处理方式
//线程工厂
ExecutorService ec = Executors.newCachedThreadPool(new HandlerThreadFactory());
ec.execute(new ExceptionThread2());
ec.shutdown();
//静态方法,为线程创建异常的默认处理方式
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
ExecutorService ecc = Executors.newCachedThreadPool();
ecc.execute(new ExceptionThread2());
ecc.shutdown();
}
}
class ExceptionThread2 implements Runnable{
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("run by "+t);
//getUncaughtExceptionHandler()得到处理异常的类
System.out.println("eh = "+t.getUncaughtExceptionHandler());
throw new RuntimeException();//抛出异常
}
}
//异常处理
//继承接口,重写处理方法
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("catch " + e);
}
}
//线程工厂,为每一个线程创建异常处理器
//setUncaughtExceptionHandler(new MyUncaughtExceptionHandler())
class HandlerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println(this + " creating new Thread");
Thread t = new Thread(r);
System.out.println("created "+t);
t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
System.out.println("eh = "+t.getUncaughtExceptionHandler());
return t;
}
}
任务中的返回值
必须使用Executor.submit()调用,使用call()而不是run(); callable接口,具有类型参数的泛型 泛型为call的返回值类型 submit的返回值类型为Future< type > type为call()的返回值类型import java.util.ArrayList;
import java.util.concurrent.*;
public class CallableDemo {
public static void main(String...args) {
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results =
new ArrayList<>();
for (int i = 0; i < 10; i++)
//submit产生future对象,用返回值类型进行参数化
results.add(exec.submit(new TaskWithResult(i)));
for (Future<String> fs : results) {
try {
//get() blocks until completion:
//可以调用isDone判断future结果是否准备就绪
//准备好之前调用get()将阻塞
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;//???
} catch (ExecutionException e) {
System.out.println(e);
} finally {
exec.shutdown();
}
}
}
}
//具有类型参数的泛型 泛型为call的返回值类型
class TaskWithResult implements Callable<String>{
private int id;
public TaskWithResult(int id){
this.id = id;
}
public String call(){
return "result of TaskWithResult " + id;
}
}
优先级
这里运行的结果和预想的不同,理解代码,以后再看import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimplePriorities implements Runnable {
private int countDown = 5;
private volatile double d;
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
@Override
public String toString() {
return Thread.currentThread() + ": "
+ countDown;
}
@Override
public void run() {
//优先级在run()方法里面声明,在构造函数中声明没有用
Thread.currentThread().setPriority(priority);
while (true) {
for (int i = 0; i < 100000; i++) {
d += (Math.PI + Math.E) / (double) i;
if (i % 1000 == 0)
Thread.yield();//让步 只是一个暗示
}
System.out.println(this);
if (--countDown == 0) return;
}
}
public static void main(String... args) {
//没发现区别。。。
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
for (int i = 0; i < 5; i++)
exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
exec.shutdown();
}
}
后台线程
先熟悉写法,意义以后理解,概念看书非后台线程结束后会杀死后台线程,所以后台线程run()方法的finally可能会得不到执行,比如main()方法就是非后台线程,改变main()中的sleep() 阻塞时间 可以看到后台线程的启动过程
在线程start方法前调用setDaemon(true)来设置为后台线程
一旦被声明为后台线程,它创建的子线程也是后台线程,不再需要设置
下面代码还用到了线程工厂来创建后台线程,注释掉的部分为不用工厂的创建过程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//后台线程
public class SimpleDaemons implements Runnable{
@Override
public void run() {
try {
while(true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread()+" "+this);
//后台线程创建的线程还是后台线程,不再需要手动设置
Thread td = new Thread(new Daemon());
td.start();
System.out.println("td.isDaemon():"+td.isDaemon()+" Daemon:"+td);
}
}catch(InterruptedException e){
System.out.println("sleep() interrupted");
}finally{
//没有执行!!!
//主线程结束后直接粗暴的终止所有后台线程
System.out.println("this is finally");
}
}
public static void main(String...args) throws Exception{
//利用线程工厂
//接受的对象工厂用来创建新的线程
ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());
for(int i = 0;i<10;i++){
exec.execute(new SimpleDaemons());//线程工厂创建线程;
//Thread daemon = new Thread(new SimpleDaemons());
//daemon.setDaemon(true);//将线程设置为后台线程 必须在启动之前,即调用start()方法前
//daemon.start();
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(500);//main()线程先停一段时间,可以看到后台线程的启动过程
//main()结束,杀掉所有后台线程
}
}
//在后台线程中创建的子线程
class Daemon implements Runnable{
@Override
public void run() {
while(true){}
}
@Override
public String toString() {
return "hello,"+Thread.currentThread().getName();
}
}
join
在第一个线程上的第二个线程调用join(),则执行第二个,第一个线程被挂起,执行结束才返回开始执行第一个 有参数的join(1000); 1000ms之后不管第二个线程有没有结束都会返回,第一个线程开始,但此时的第二个线程仍然在执行至结束 try-catch会清除sleep被打断后的中断状态,所以返回false//join() 在第一个线程上的第二个线程调用join,则执行第二个,执行结束才返回
class Sleeper extends Thread{
private int duration;
public Sleeper(String name,int sleeptime){
super(name);
duration = sleeptime;
start();
}
public void run(){
try{
sleep(duration);
}catch (InterruptedException ie){
System.out.println(getName()+" was interrupted. "+
"isInterrupted():"+isInterrupted());
return;
}
System.out.println(getName()+" has awakened");
}
}
class Joiner extends Thread{
private Sleeper sleeper;
public Joiner(String name,Sleeper sleeper){
super(name);
this.sleeper = sleeper;
start();
}
public void run(){
try{
//可以给join加一个超时参数使之可以返回
//加上1000参数,先返回complemented,再返回awaked
//1000ms之后,joiner开始执行,但是sleeper没有停止
//不加参数,先执行完join 再执行complemented
sleeper.join(1000);//直到sleeper()结束之后Joiner线程才会继续下去
}catch(InterruptedException e){
System.out.println("Interrupted!");
}
System.out.println(getName()+" join complemented");
}
}
public class Joining{
public static void main(String...args){
Sleeper sleeper = new Sleeper("sleepy",1500);
Sleeper sleeper1 = new Sleeper("Grumpy",1500);
Joiner dopey = new Joiner("Dopey",sleeper);
Joiner doc = new Joiner("Doc",sleeper1);
sleeper1.interrupt();//打断join()方法
}
}
有响应的“用户界面”
多线程的一个用法 在计算的同时可以监听控制台输入,不会阻塞public class ResponiveUI extends Thread {
private static volatile double d = 1;
public ResponiveUI(){
setDaemon(true);
start();
}
public void run(){
while(true){
d+=(Math.E+Math.PI)/d;
if(d>100000)
break;
}
}
public static void main(String[]args)throws Exception{
new ResponiveUI();//后台任务,不影响后面的read();
System.in.read();
System.out.println(d);
}
}
如果单线程执行,只能等到d>100000才能执行下面的输入,把它设为后台线程,就可以直接执行输入,不在需要等待,如果while中是个死循环,单线程永远到不了输入
内部类的运用
InnerThread1,InnerThread2运用继承InnerRunnable1,InnerRunnable1运用runnable接口
ThreadMethod在方法里运用匿名内部类
import java.util.concurrent.TimeUnit;
public class ThreadVariations {
public static void main(String[]args){
new InnerThread1("InnerThread1");
new InnerThread2("InnerThread2");
new InnerRunnable1("InnerRunnable1");
new InnerRunnable2("InnerRunnable2");
new ThreadMethod("ThreadMethod").runTask();
}
}
class InnerThread1{
private int countDown = 5;
private Inner inner;
private class Inner extends Thread {
Inner(String name) {
super(name);
start();
}
@Override
public void run() {
try{
while(true){
System.out.println(this);
if(--countDown==0)
return;
sleep(10);
}
}catch(InterruptedException ie){
System.out.println("interrupted!");
ie.printStackTrace();
}
}
@Override
public String toString(){
return getName() + ":" + countDown;
}
}
public InnerThread1(String name){
inner = new Inner(name);
}
}
class InnerThread2 {
private int countDown = 5;
private Thread t;
public InnerThread2(String name) {
t = new Thread(name) {
@Override
public void run() {
try {
while (true) {
System.out.println(this);
if (--countDown == 0)
return;
sleep(10);
}
} catch (InterruptedException ie) {
System.out.println("interrupted!");
ie.printStackTrace();
}
}
@Override
public String toString() {
return getName() + ":" + countDown;
}
};
t.start();
}
}
class InnerRunnable1{
private int countDown = 5;
private Inner inner;
private class Inner implements Runnable {
Thread t;
Inner(String name) {
t=new Thread(this,name);
t.start();
}
@Override
public void run() {
try{
while(true){
System.out.println(this);
if(--countDown==0)
return;
TimeUnit.MILLISECONDS.sleep(10);
}
}catch(InterruptedException ie){
System.out.println("interrupted!");
ie.printStackTrace();
}
}
@Override
public String toString(){
return t.getName() + ":" + countDown;
}
}
public InnerRunnable1(String name) {
inner = new Inner(name);
}
}
class InnerRunnable2 {
private int countDown = 5;
private Thread t;
public InnerRunnable2(String name) {
t = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println(this);
if (--countDown == 0)
return;
TimeUnit.MILLISECONDS.sleep(10);
}
} catch (InterruptedException ie) {
System.out.println("interrupted!");
ie.printStackTrace();
}
}
@Override
public String toString() {
return Thread.currentThread().getName() + ":" + countDown;
}
},name);
t.start();
}
}
class ThreadMethod{
private int countDown = 5;
private Thread t;
private String name;
public ThreadMethod(String name){
this.name = name;
}
public void runTask(){
if(t==null){
t = new Thread(name){
public void run(){
try{
while(true){
System.out.println(this);
if(--countDown==0)
return;
sleep(10);
}
}catch(InterruptedException ie){
System.out.println("interrupted!");
ie.printStackTrace();
}
}
public String toString(){
return getName() + ":" + countDown;
}
};
t.start();
}
}
}
受限资源的共享
下面一个程序 EvenGenerator.next()方法把currentEvenValue加2,但输出的时候竟然输出了奇数。在for循环里面创建了多个线程,同时对currentEvenValue修改,可能该线程在进行奇偶判断时,其他线程正好执行完+1,就出现了奇数,共享的资源产生混乱
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class EvenChecker implements Runnable {
private final int id;
private IntGenerator generator;
public EvenChecker(IntGenerator g,int id){
this.id = id;
generator = g;
}
@Override
public void run() {
while(!generator.isCanceled()){
int val = generator.next();
if(val%2!=0){
System.out.println(val + " not even!");
generator.cancel();
}
}
}
public static void test(IntGenerator gp,int count) {
System.out.println("Press Control-C to exit");
ExecutorService ec = Executors.newCachedThreadPool();
for (int i = 0; i < count; i++)
//一个EvenGenerator被多个线程修改
ec.execute(new EvenChecker(gp, i));//同一个gp
ec.shutdown();
}
public static void test(IntGenerator gp){
test(gp,10);
}
}
abstract class IntGenerator{
private volatile boolean canceled = false;
public abstract int next();
public void cancel(){
canceled=true;
}
public boolean isCanceled(){
return canceled;
}
}
class EvenGenerator extends IntGenerator{
private int currentEvenValue=0;
@Override
public /*synchronized*/ int next() {
++currentEvenValue;
++currentEvenValue;
return currentEvenValue;
}
public static void main(String...arg){
EvenChecker.test(new EvenGenerator());
}
}
解决方法
使用synchronized
第一个进入next()的获得锁,操作完之后才会把锁解开,其他线程才会开始执行next();
@Override
public synchronized int next() {
++currentEvenValue;
++currentEvenValue;
return currentEvenValue;
}
使用lock()加锁
lock(),unlock(),tryLock();
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//使用Lock对象
public class MutexEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
private Lock lock = new ReentrantLock();
@Override
public int next() {
lock.lock();//加锁
//使用lock更利于异常的处理
try{
++currentEvenValue;
Thread.yield();//无效
++currentEvenValue;
//return语句写在try语句,最后再去执行unlock(),防止unlock过早执行
return currentEvenValue;
}finally{
lock.unlock();
}
}
public static void main(String...atf){
EvenChecker.test(new MutexEvenGenerator());
}
}
trylock()会尝试获得一个锁,获取失败后返回false 成功返回true
tryLock(time); 在time之内尝试得到,得不到返回false
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class AttemptLocking {
//重入锁??
private ReentrantLock lock = new ReentrantLock();
//无延迟
public void untimed(){
//tryLock()尝试得到锁,成功返回true,失败返回false,不会阻塞
boolean capture = lock.tryLock();
try{
System.out.println("tryLock() "+capture);
}finally{
if(capture){
//解锁
lock.unlock();
}
}
}
//延迟
public void timed(){
boolean captured = false;
try{
//拿不到锁会等2秒,仍然拿不到返回false,拿到则返回true
captured = lock.tryLock(2, TimeUnit.SECONDS);
}catch (InterruptedException i){
throw new RuntimeException(i);
}
try{
System.out.println("tryLock(2, TimeUnit.SECONDS) "+captured);
}finally {
if(captured)
lock.unlock();
}
}
public static void main(String...args){
final AttemptLocking al = new AttemptLocking();
new Thread(){
{setDaemon(true);
setPriority(MAX_PRIORITY);}//初始化
@Override
public void run() {
al.lock.lock();
System.out.println("acquired");
}
}.start();
al.untimed();
al.timed();
al.untimed();
al.timed();
/* 可能的结果
acquired
tryLock() false
tryLock(2, TimeUnit.SECONDS) false
tryLock() false
tryLock(2, TimeUnit.SECONDS) false
*/
}
}
注意
所有有关访问数据的操作必须写synchronized
下面是原子性的样例
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
//原子性变量类
//使用AtomicInteger,使用2次i.addAndGet(1),和i++;i++;一致
//使用AtomicInteger,使用i.addAndGet(2),正常
//只把evenIncrement()设为synchronized,getVal()不设,仍然读到奇数值
//把evenIncrement(),getVal()设为synchronized,正常
//i+=2 不是原子性的操作,结果不确定
//1. 使用i.addAndGet(2)
//2. 所有访问数据的函数使用synchronized,此时可以使用i++;i++;
public class AtomicIntegerTest implements Runnable{
private AtomicInteger i = new AtomicInteger(0);
//private int i = 0;
public int /*synchronized*/ getValue(){
return i.get();
//return i;
}
private /*synchronized*/ void evenIncrement(){
i.addAndGet(2);//+=2
//i++;i++;
//i+=2;
}
@Override
public void run() {
while(true){
evenIncrement();
}
}
public static void main(String...args){
//在指定时间后执行一次 计时器
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.err.println("Aborting");
System.exit(0);
}
},5000);//Terminate after 5 seconds
ExecutorService ece = Executors.newCachedThreadPool();
AtomicIntegerTest ait = new AtomicIntegerTest();
ece.execute(ait);
while(true){
int value = ait.getValue();
if(value%2!=0) {
System.out.println(value);
System.exit(0);
}
}
}
}
(持续学习,未完待续)