Java学习-t16-多线程

t16.2

线程3种方式

Thread

Runnable

Callable

 

 

FutureTask实现类

callable中的get() 将导致程序阻塞,必须等到子线程结束后才会得到返回值

 

线程状态:

新建New

就绪Runnable

运行Running

阻塞Blocked

死亡Dead

image

new->runnable:

    start()

runnable->blocked:

    sleep时间到

    IO方法返回

    获得同步锁

    收到通知

    resume()

blocked->runnable:

   sleep()

   IO阻塞

   等待同步锁

   等待通知

   suspend()

runnable->running:

  得到处理器资格

running->runnable:

  yield() 或 失去处理器资格

running->dead:

  stop() error exception run/call()完成

 

join()方法:优先子线程,阻塞主线程

1.1 main
   1.2 sub.start()
   1.3 sub2.start()
   ....
   1.1.1 sub2.join()
   1.3.2 ...
   1.4...
package com.lee.test.ligang.unit16.t4.status;

import java.util.Date;

public class JoinThread extends Thread {
	// 提供一个有参数的构造器,用于设置该线程的名字
	public JoinThread(String name) {
		super(name);
	}

	// 重写run()方法,定义线程执行体
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(getName() + "  " + i);
		}
	}

	public static void main(String[] args) throws Exception {
		// 启动子线程
		new JoinThread("新线程").start();
		for (int i = 0; i < 100; i++) {
			if (i == 20) {
				JoinThread jt = new JoinThread("被Join的线程");
				jt.start();
				// main线程调用了jt线程的join()方法,main线程
				// 必须等jt执行结束才会向下执行
				System.out.println(new Date());
				sleep(5000);
				System.out.println(new Date());
				// 等同调用无限大sleep,直到子线程结束,sleep效果才消失
				jt.join();
			}
			System.out.println(Thread.currentThread().getName() + "  " + i);
		}
	}

}

.setDaemon(true):将某个线程设置为后台线程。当主线程或者后台所在的前台线程结束后,后台线程也结束。

package com.lee.test.ligang.unit16.t4.status;

public class DaemonThread extends Thread {
	// 定义后台线程的线程执行体与普通线程没有任何区别
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println(getName() + "  " + i);
		}
	}

	public static void main(String[] args) {
		DaemonThread t = new DaemonThread();
		// 将此线程设置成后台线程
		t.setDaemon(true);
		// 启动后台线程
		t.start();
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "  " + i);
		}
		// -----程序执行到此处,前台线程(main线程)结束------
		// 后台线程也应该随之结束
	}
}

 

.yield():假sleep,不足赛线程。根据优先级更高的yield将最先被调度执行

 

同步机制

1.同步代码块 即 以对象作为同步监视器

2.同步方法 即 同步监视器是this

3.Lock 更灵活

 

死锁:

1.A a; B b两个对象

3.b传入aM的同步方法中,a作为同步监视器被锁定

3.a进入bM的同步方法中,b作为同步监视器被锁定

4.调用b.m方法,b被锁定需等待b释放锁才能进行,当前线程阻塞

5.调用a.m方法,a被锁定需等待a释放锁才能进行,当前线程阻塞

 

t16.6.1 传统通信线程

package com.lee.test.ligang.unit16.t6.synchronized_1;

public class Account {
	// 封装账户编号、账户余额的两个成员变量
	private String accountNo;
	private double balance;
	// 标识账户中是否已有存款的旗标
	private boolean flag = false;

	public Account() {
	}

	// 构造器
	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	// accountNo的setter和getter方法
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public String getAccountNo() {
		return this.accountNo;
	}

	// 因此账户余额不允许随便修改,所以只为balance提供getter方法,
	public double getBalance() {
		return this.balance;
	}

	public synchronized void draw(double drawAmount) {
		try {
			// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
			if (!flag) {
				wait();
			} else {
				// 执行取钱
				System.out.println(Thread.currentThread().getName() + " 取钱:"
						+ drawAmount);
				balance -= drawAmount;
				System.out.println("账户余额为:" + balance);
				// 将标识账户是否已有存款的旗标设为false。
				flag = false;
				// 唤醒其他线程
				notifyAll();
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	public synchronized void deposit(double depositAmount) {
		try {
			// 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
			if (flag) // ①
			{
				wait();
			} else {
				// 执行存款
				System.out.println(Thread.currentThread().getName() + " 存款:"
						+ depositAmount);
				balance += depositAmount;
				System.out.println("账户余额为:" + balance);
				// 将表示账户是否已有存款的旗标设为true
				flag = true;
				// 唤醒其他线程
				notifyAll();
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	// 下面两个方法根据accountNo来重写hashCode()和equals()方法
	public int hashCode() {
		return accountNo.hashCode();
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj != null && obj.getClass() == Account.class) {
			Account target = (Account) obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}

package com.lee.test.ligang.unit16.t6.synchronized_1;

public class DrawTest {
	public static void main(String[] args) {
		// 创建一个账户
		Account acct = new Account("1234567", 0);
		new DrawThread("取钱者", acct, 800).start();
		new DepositThread("存款者甲", acct, 800).start();
		new DepositThread("存款者乙", acct, 800).start();
		new DepositThread("存款者丙", acct, 800).start();
	}
}

package com.lee.test.ligang.unit16.t6.synchronized_1;

public class DepositThread extends Thread {
	// 模拟用户账户
	private Account account;
	// 当前取钱线程所希望存款的钱数
	private double depositAmount;

	public DepositThread(String name, Account account, double depositAmount) {
		super(name);
		this.account = account;
		this.depositAmount = depositAmount;
	}

	// 重复100次执行存款操作
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.deposit(depositAmount);
		}
	}
}

package com.lee.test.ligang.unit16.t6.synchronized_1;

public class DrawThread extends Thread {
	// 模拟用户账户
	private Account account;
	// 当前取钱线程所希望取的钱数
	private double drawAmount;

	public DrawThread(String name, Account account, double drawAmount) {
		super(name);
		this.account = account;
		this.drawAmount = drawAmount;
	}

	// 重复100次执行取钱操作
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.draw(drawAmount);
		}
	}
}

t16.6.2

Condition控制线程通信

package com.lee.test.ligang.unit16.t6.t2.condition;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Account {
	// 显式定义Lock对象
	private final Lock lock = new ReentrantLock();
	// 获得指定Lock对象对应的Condition
	private final Condition cond = lock.newCondition();
	// 封装账户编号、账户余额的两个成员变量
	private String accountNo;
	private double balance;
	// 标识账户中是否已有存款的旗标
	private boolean flag = false;

	public Account() {
	}

	// 构造器
	public Account(String accountNo, double balance) {
		this.accountNo = accountNo;
		this.balance = balance;
	}

	// accountNo的setter和getter方法
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}

	public String getAccountNo() {
		return this.accountNo;
	}

	// 因此账户余额不允许随便修改,所以只为balance提供getter方法,
	public double getBalance() {
		return this.balance;
	}

	public void draw(double drawAmount) {
		// 加锁
		lock.lock();
		try {
			// 如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
			if (!flag) {
				cond.await();
			} else {
				// 执行取钱
				System.out.println(Thread.currentThread().getName() + " 取钱:"
						+ drawAmount);
				balance -= drawAmount;
				System.out.println("账户余额为:" + balance);
				// 将标识账户是否已有存款的旗标设为false。
				flag = false;
				// 唤醒其他线程
				cond.signalAll();
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		// 使用finally块来释放锁
		finally {
			lock.unlock();
		}
	}

	public void deposit(double depositAmount) {
		lock.lock();
		try {
			// 如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
			if (flag) // ①
			{
				cond.await();
			} else {
				// 执行存款
				System.out.println(Thread.currentThread().getName() + " 存款:"
						+ depositAmount);
				balance += depositAmount;
				System.out.println("账户余额为:" + balance);
				// 将表示账户是否已有存款的旗标设为true
				flag = true;
				// 唤醒其他线程
				cond.signalAll();
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		// 使用finally块来释放锁
		finally {
			lock.unlock();
		}
	}

	// 下面两个方法根据accountNo来重写hashCode()和equals()方法
	public int hashCode() {
		return accountNo.hashCode();
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj != null && obj.getClass() == Account.class) {
			Account target = (Account) obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}

package com.lee.test.ligang.unit16.t6.t2.condition;

public class DepositThread extends Thread {
	// 模拟用户账户
	private Account account;
	// 当前取钱线程所希望存款的钱数
	private double depositAmount;

	public DepositThread(String name, Account account, double depositAmount) {
		super(name);
		this.account = account;
		this.depositAmount = depositAmount;
	}

	// 重复100次执行存款操作
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.deposit(depositAmount);
		}
	}
}

package com.lee.test.ligang.unit16.t6.t2.condition;

public class DrawTest {
	public static void main(String[] args) {
		// 创建一个账户
		Account acct = new Account("1234567", 0);
		new DrawThread("取钱者", acct, 800).start();
		new DepositThread("存款者甲", acct, 800).start();
		new DepositThread("存款者乙", acct, 800).start();
		new DepositThread("存款者丙", acct, 800).start();
	}
}


package com.lee.test.ligang.unit16.t6.t2.condition;

public class DrawThread extends Thread {
	// 模拟用户账户
	private Account account;
	// 当前取钱线程所希望取的钱数
	private double drawAmount;

	public DrawThread(String name, Account account, double drawAmount) {
		super(name);
		this.account = account;
		this.drawAmount = drawAmount;
	}

	// 重复100次执行取钱操作
	public void run() {
		for (int i = 0; i < 100; i++) {
			account.draw(drawAmount);
		}
	}
}

 

【原则】不同的线程,装在着同一个对象,则这个对象应有Lock成员变量来保证不同的线程持有同一个对象锁即同步监视器,Condition来保证

t16.6.3 阻塞队列 BlockingQueue 控制线程通信

Array

Linked

Prority

Synchronous

Delay

package com.lee.test.ligang.unit16.t6.t3.blockqueue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class Producer extends Thread {
	private BlockingQueue<String> bq;

	public Producer(BlockingQueue<String> bq) {
		this.bq = bq;
	}

	public void run() {
		String[] strArr = new String[] { "Java", "Struts", "Spring" };
		for (int i = 0; i < 999999999; 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 BlockingQueueTest2 {
	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();
	}
}

Thread-2生产者准备生产集合元素!
Thread-1生产者准备生产集合元素!
Thread-0生产者准备生产集合元素!
Thread-3消费者准备消费集合元素!
Thread-0生产完成:[Java]
Thread-1生产完成:[Java]
Thread-0生产者准备生产集合元素!
Thread-1生产者准备生产集合元素!
Thread-3消费完成:[Java]
Thread-3消费者准备消费集合元素!
Thread-3消费完成:[]
Thread-3消费者准备消费集合元素!
Thread-2生产完成:[Java]
Thread-2生产者准备生产集合元素!
Thread-3消费完成:[]

 

t16.7 ThreadGroup 线程组和未处理的异常

 

t16.8 线程池

Executors

  image

Executor

image

ExecutorService extends Executor

image

ScheduledExecutorService extends ExecutorService

image

 

package com.lee.test.ligang.unit16.t8.t1.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolTest {
	public static void main(String[] args) throws Exception {
		// 创建足够的线程来支持4个CPU并行的线程池
		// 创建一个具有固定线程数(6)的线程池
		ExecutorService pool = Executors.newFixedThreadPool(6);
		// 使用Lambda表达式创建Runnable对象
		Runnable target = () -> {
			for (int i = 0; i < 100; i++) {
				System.out.println(Thread.currentThread().getName() + "的i值为:"
						+ i);
			}
		};
		// 向线程池中提交两个线程
		pool.submit(target);
		pool.submit(target);
		// 关闭线程池
		pool.shutdown();
	}
}

package com.lee.test.ligang.unit16.t8.t2.forkjoinpool;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

// 继承RecursiveAction来实现"可分解"的任务
class PrintTask extends RecursiveAction {
	// 每个“小任务”只最多只打印50个数
	private static final int THRESHOLD = 50;
	private int start;
	private int end;

	// 打印从start到end的任务
	public PrintTask(int start, int end) {
		this.start = start;
		this.end = end;
	}

	@Override
	protected void compute() {
		// 当end与start之间的差小于THRESHOLD时,开始打印
		if (end - start < THRESHOLD) {
			for (int i = start; i < end; i++) {
				System.out.println(Thread.currentThread().getName() + "的i值:"
						+ i);
			}
		} else {
			// 如果当end与start之间的差大于THRESHOLD时,即要打印的数超过50个
			// 将大任务分解成两个小任务。
			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 Exception {
		ForkJoinPool pool = new ForkJoinPool();
		// 提交可分解的PrintTask任务
		pool.submit(new PrintTask(0, 300));
		pool.awaitTermination(2, TimeUnit.SECONDS);
		// 关闭线程池
		pool.shutdown();
	}
}

package com.lee.test.ligang.unit16.t8.t2.forkjoinpool;

import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

// 继承RecursiveTask来实现"可分解"的任务
class CalTask extends RecursiveTask<Integer> {
	// 每个“小任务”只最多只累加20个数
	private static final int THRESHOLD = 100;
	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;
	}

	@Override
	protected Integer compute() {
		int sum = 0;
		// 当end与start之间的差小于THRESHOLD时,开始进行实际累加
		if (end - start < THRESHOLD) {
			for (int i = start; i < end; i++) {
				sum += arr[i];
			}
			return sum;
		} else {
			// 如果当end与start之间的差大于THRESHOLD时,即要累加的数超过20个时
			// 将大任务分解成两个小任务。
			int middle = (start + end) / 2;
			CalTask left = new CalTask(arr, start, middle);
			CalTask right = new CalTask(arr, middle, end);
			// 并行执行两个“小任务”
			left.fork();
			right.fork();
			// 把两个“小任务”累加的结果合并起来
			int left1 = left.join();
			int right1 = right.join();
			return left1 + right1; // ①
		}
	}
}

public class Sum {
	public static void main(String[] args) throws Exception {
		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(20);
			// 对数组元素赋值,并将数组元素的值添加到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();
	}
}

 

t16.9 线程相关类

t16.9.1 ThreadLocal 线程局部变量 ThreadLocalVar

隔离多线程的竞争资源

image

 

ThreadLocal:将需要并发的资源复制成多个副本,每个线程拥有一份资源,即自己的副本也就没有必要对该变量进行同步了。

对比---

同步机制:为了同步多个线程对相同资源的并发访问;若多线程之间需要共享资源达到通信则用该方式

ThreadLocal:为了隔离多个线程的数据共享;若仅需要隔离多个线程的共享冲突则用该方式

t16.9.2 集合线程安全

Collections

    .synchronizedCollection

 

ConcurrentHashMap 线程安全的集合类

转载于:https://my.oschina.net/kingdelee/blog/380583

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值