文章目录
- 概念
- 线程创建
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 补充知识点
- 静态代理模式
- Lamda表达式
- 线程状态
- 线程停止(stop)
- 线程休眠(sleep)
- 线程礼让(yield)
- 线程强制执行(join)
- 线程优先级
- 守护进程
- 线程同步
- 线程不安全举例
- 不安全买票
- 不安全银行
- 不安全集合
- 同步方法
- 同步方法,同步块(synchronized)
- 安全买票代码
- 安全取钱代码
- 安全list代码
- JUC
- 死锁
- Lock锁
- 不加锁的情况
- 加锁的代码
- 线程协作
- 生产者消费者问题
- 管程法
- 信号灯法
- 线程池
- 特点
- 学习链接
概念
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务 器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能 执行一个代码,因为切换的很快,所以就有同时执行的错觉。
- 线程就是独立的执行路径;
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
- main() 称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如cpu调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程创建
继承Thread类
1.继承Thread类
2.重写run方法
3.创建对象调用star();
测试
package collectionstest;
import java.lang.*;
public class ThreadTest {
public static void main(String[] args) {
MyThread mt1 = new MyThread("1");
MyThread mt2 = new MyThread("2222222222222222222222222222222222222222222222");
MyThread mt3 = new MyThread("33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333");
mt1.start();
mt2.start();
mt3.start();
for(int i = 0;i<100;i++) {
System.out.println("666666666666666666666");
}
}
}
class MyThread extends Thread{
private String k;
MyThread(String n){
this.k = n;
}
@Override
public void run() {
for(int i = 0;i<100;i++) {
System.out.println(k);
}
}
}
结果
实现Runnable接口
1.实现Runnable接口
2.实现run方法
3.创建对象,调用start方法
测试:
package collectionstest;
public class RunnableTest {
public static void main(String[] args) {
MyRunnable mr =new MyRunnable("0");
Thread myThread = new Thread(mr);
myThread.start();
MyRunnable mr1 =new MyRunnable("11111111");
Thread myThread1 = new Thread(mr1);
myThread1.start();
}
}
class MyRunnable implements Runnable{
private String k;
MyRunnable(String n){
this.k = n;
}
@Override
public void run() {
for(int i = 0;i<100;i++) {
System.out.println(k);
}
}
}
结果
实现Callable接口
测试代码
package collectionstest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class myCallable implements Callable<Boolean>{
private String msg;
myCallable(String s){
msg = s;
}
public Boolean call() {
System.out.println(msg);
return true;
}
}
public class CallableTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建目标对象
myCallable mc0 = new myCallable("1");
myCallable mc1 = new myCallable("22");
myCallable mc2 = new myCallable("333");
//创建执行服务
ExecutorService es = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> f0 = es.submit(mc0);
Future<Boolean> f1 = es.submit(mc1);
Future<Boolean> f2 = es.submit(mc2);
//获取结果
boolean r0 = f0.get();
boolean r1 = f1.get();
boolean r2 = f2.get();
System.out.println(""+r0 + " " + r1 + " "+ r2);
//关闭服务
es.shutdownNow();
}
}
结果
补充知识点
静态代理模式
也就是让一个类帮助另一个类做事情(可以做一些额外的准备)。
代码
interface Marry{
void happyMarry();
}
class Human implements Marry{
String name;
Human(String s){
name = s;
}
public void happyMarry() {
System.out.println(name + "结婚了");
}
}
class WeddingCompany implements Marry {
private Human human;
WeddingCompany(Human a){
human = a;
}
private void before() {
System.out.println("结婚前的准备");
}
private void after() {
System.out.println("结婚后的整理");
}
public void happyMarry() {
before();
human.happyMarry();
after();
}
}
public class staticPorxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new Human("一个人"));
weddingCompany.happyMarry();
}
}
结果
Lamda表达式
只用一次的类: 内部类 -> 匿名内部类 -> 局部内部类 -> 匿名内部类 -> lamda表达式。
特点:
避免匿名内部类定义过多
代码很简洁,只留下核心的逻辑。
函数式接口:
只有唯一一个抽象方法。可以通过lamda表达式来创建该接口的对象。
代码
package ProcessThreadTest;
interface MyInterface{
public void function();
}
interface MyInterface1{
public void function(String a);
}
class ClassOne implements MyInterface{
public void function() {
System.out.println("普通类实现接口");
}
}
public class lamdaTest {
//静态内部类
static class ClassTwo implements MyInterface{
public void function() {
System.out.println("静态内部类实现接口");
}
}
public static void main(String[] args) {
//普通方法调用不使用lamda表达式
MyInterface myclass = new ClassOne();
myclass.function();
//静态方法调用
myclass = new ClassTwo();
myclass .function();
//局部内部类调用
class ClassThree implements MyInterface{
public void function() {
System.out.println("局部内部类实现接口");
}
}
myclass = new ClassThree();
myclass.function();
//匿名内部类
myclass = new MyInterface() {
public void function() {
System.out.println("匿名内部类实现接口");
}
};
myclass.function();
//lamda表达式实现
myclass = ()->{
System.out.println("lamda表达式");
};
myclass.function();
//带参数
MyInterface1 myclass1 = (String a)->{
System.out.println("参数是:"+a);
};
myclass1.function("带参数");
//省略参数类型
myclass1= myclass1 = (a)->{
System.out.println("参数是:"+a);
};
myclass1.function("省略类型");
//省略括号
myclass1= myclass1 = a->{
System.out.println("参数是:"+a);
};
myclass1.function("省略括号");
//省略花括号(只有一行)
myclass1= myclass1 = a -> System.out.println("参数是:"+a);
myclass1.function("省略花括号");
}
}
结果
线程状态
线程的几种状态:
Thread类的一些重要的方法:
线程停止(stop)
不建议使用stop方法和destory方法。
从Thread类的源码中可以看到,这个stop方法已经废弃。
建议线程通过循环次数自动停止下来(上文已实现)或者设置标志位。
标志位代码
package ProcessThreadTest;
class MyTheadStop implements Runnable{//此处不能继承Thread类,因为stop方法在Thread类中是
boolean flag = true;
public void run() {
while(flag ==true) {
System.out.println("hi!");
}
}
public void stop() {
this.flag = false;
}
}
public class ThreadStopTest {
public static void main(String[] args) {
MyTheadStop mts = new MyTheadStop();
Thread myThread = new Thread(mts);
myThread.start();
for(int i = 0;i<300;i++) {
if(i == 150) {
mts.stop();
System.out.println("线程停止了");
}
System.out.println(i);
}
}
}
. . . . . . . . .
线程休眠(sleep)
sleep (时间)指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间达到后线程进入就绪状态;
sleep可以模拟网络延时,倒计时等。
每一个对象都有一个锁,sleep不会释放锁;
代码
package ProcessThreadTest;
import java.sql.Time;
import java.util.Date;
public class SleepTest implements Runnable {
private int ticketNums = 1;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
Date date = new Date() ;
if(ticketNums > 10) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + date + ":拿到了第"+ ticketNums +"张票");
ticketNums++;
}
}
public static void main(String[] args) {
SleepTest sleepTest = new SleepTest();
new Thread(sleepTest,"小明").start();
new Thread(sleepTest,"小红").start();
new Thread(sleepTest,"小亮").start();
}
}
结果(读数据之前没有加锁,暂不解决)
线程礼让(yield)
礼让线程,让当前正在执行的线程暂停,但不阻塞。
将线程从运行状态转为就绪状态,让cpu重新调度,礼让不一定成功!
测试代码
package ProcessThreadTest;
class myYield implements Runnable{
private String name;
myYield(String a){
name = a;
}
public void run() {
System.out.println(name + "礼让前");
Thread.yield();
System.out.println(name + "礼让后");
}
}
public class YieldTest {
public static void main(String[] args) {
new Thread( new myYield("小明") ).start();
new Thread( new myYield("小红") ).start();
}
}
运行结果
线程强制执行(join)
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞 ,可以想象成插队
测试代码
package ProcessThreadTest;
public class JoinTest implements Runnable {
public static void main(String[] args) throws InterruptedException {
JoinTest jt = new JoinTest();
Thread thread = new Thread(jt);
thread.start();
for(int i = 0;i<100;i++) {
System.out.println("main:"+i);
if(i==50) {
thread.join();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<100;i++) {
System.out.println(" join:"+i);
}
}
}
运行结果
线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度 器按照优先级决定应该调度哪个线程来执行。线程的优先级用数字表示,范围从1~10。
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
getPriority()获取优先级,setPriority(int xxx)设置优先级。
守护进程
特点
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待
代码
package ProcessThreadTest;
class DeamonClass implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
System.out.println("守护进程--------------");
}
}
class NormalClass implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<100;i++) {
System.out.println("普通进程");
}
System.out.println("普通进程结束~~~~~");
}
}
public class DeamonTest {
public static void main(String[] args) {
Thread thread = new Thread(new DeamonClass());
thread.setDaemon(true);
thread.start();
new Thread(new NormalClass()).start();
}
}
运行结果:
线程同步
处理多线程问题时 , 多个线程访问同一个对象 , 并且某些线程还想修改这个对象 。这时候我们就需要线程同步 。线程同步其实就是一种等待机制 , 多个需要同时访问此对象的线程进入这个对象的等待池形成队列, 等待前面线程使用完毕 , 下一个线程再使用。
由于同一进程的多个线程共享同一块存储空间 , 在带来方便的同时,也带来了访问 冲突问题 , 为了保证数据在方法中被访问时的正确性 , 在访问时加入 锁机制synchronized , 当一个线程获得对象的排它锁 , 独占资源 , 其他线程必须等待 , 使用后释放锁即可 . 存在以下问题 :
一个线程持有锁会导致其他所有需要此锁的线程挂起 ;
在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题 ;
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置 , 引起性能问题 。
线程不安全举例
不安全买票
代码
package ProcessThreadTest;
class BuyTicket implements Runnable{
private int ticketNums = 10;
boolean flag = true;
public void buy() throws InterruptedException {
if(ticketNums<=0) {
flag = false;
return;
}
Thread.sleep(100); //模拟网络延迟
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
public void run() {
while(flag) {
try {
buy();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class UnsafeBuyTIcket {
public static void main(String[] args) {
BuyTicket bt = new BuyTicket();
new Thread(bt,"A").start();
new Thread(bt,"B").start();
new Thread(bt,"C").start();
}
}
结果
不安全银行
代码
package ProcessThreadTest;
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"零花钱");
Drawing A = new Drawing(account,50,"A");
Drawing B = new Drawing(account,100,"B");
A.start();
B.start();
}
}
class Account{
int money;
String name;
Account(int a,String b){
money = a;
name = b;
}
}
class Drawing extends Thread{
Account account;
int drawingMoney;
String name;
public Drawing(Account account,int drawingMoney,String name) {
this.account = account;
this.drawingMoney = drawingMoney;
this.name = name;
}
public void run() {
if(account.money < drawingMoney) {
System.out.println(Thread.currentThread().getName() + "钱不够");
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
System.out.println(name + "取了:"+ drawingMoney);
System.out.println(account.name + "余额为" + account.money);
}
}
结果
不安全集合
代码
package ProcessThreadTest;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
运行结果
可以看到list中的元素并不是一万个,因为有的是同时新增的,其中的某些就被覆盖掉了。
同步方法
由于我们可以通过 private 关键字来保证数据对象只能被方法访问 , 所以我们只需 要针对方法提出一套机制 , 这套机制就是 synchronized 关键字 , 它包括两种用法 : synchronized 方法 和synchronized 块。
同步方法 : public synchronized void method(int args) {}
synchronized方法控制对 “对象” 的访问 , 每个对象对应一把锁 , 每个synchronized方法都必须获得调用该方法的对象的锁才能执行 , 否则线程会阻塞 , 方法一旦执行 , 就独占该锁 , 直到该方法返回才释放锁 , 后面被阻塞的线程才能获得这个锁 , 继续执行。
缺陷 : 若将一个大的方法申明为synchronized 将会影响效率
同步方法,同步块(synchronized)
同步块 : synchronized (Obj ) { }
Obj 称之为同步监视器
Obj 可以是任何对象 , 但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器 , 因为同步方法的同步监视器就是this , 就是 这个对象本身 , 或者是 class
同步监视器的执行过程
- 第一个线程访问 , 锁定同步监视器 , 执行其中代码 。
- 第二个线程访问 , 发现同步监视器被锁定 , 无法访问。
- 第一个线程访问完毕 , 解锁同步监视器 。
- 第二个线程访问, 发现同步监视器没有锁 , 然后锁定并访问。
安全买票代码
只要把之前不安全的买票的buy方法加上synchronized修饰就可以保证同步了。
1.方法一
public synchronized void buy() throws InterruptedException {
if(ticketNums<=0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
安全取钱代码
synchronized 默认锁的是this方法,但这应该锁Account类而不是Drawing类。
可以把几个函数封装在synchronized之中。
public void run() {
//account不是本对象,不能用synchronized方法。
synchronized(account) {
if(account.money < drawingMoney) {
System.out.println(account.name + " 钱不够");
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
System.out.println(name + "取了:"+ drawingMoney);
System.out.println(account.name + "余额为" + account.money);
}
}
运行结果
安全list代码
package ProcessThreadTest;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
synchronized(list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
运行结果
JUC
安全类型的集合。
代码
package ProcessThreadTest;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListTest {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
运行结果
死锁
产生原因
多个线程各自占有一些共享资源 , 并且互相等待其他线程占有的资源才能运行 , 而导致两个或者多个线程都在等待对方释放资源 , 都停止执行的情形 。某一个同步块同时拥有 “ 两个以上对象的锁 ” 时 , 就可能会发生 “ 死锁 ” 的问题。
解决方法:破坏任意条件
1. 互斥条件:一个资源每次只能被一个进程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件 : 进程已获得的资源,在末使用完之前,不能强行剥夺。
4. 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系。
实验代码
package ProcessThreadTest;
public class DeadLockTest {
public static void main(String[] args) {
Makeup q1 = new Makeup(0,"A");
Makeup q2 = new Makeup(1,"B");
q1.start();
q2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int Choice,String girlName){
this.choice = Choice;
this.girlName = girlName;
}
public void run() {
try {
makeup();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if(choice == 0) {
synchronized(lipstick) {
System.out.println(this.girlName + "获得口红锁");
Thread.sleep(1000);
synchronized(mirror) {
System.out.println(this.girlName + "获得镜子锁");
}
}
}else {
synchronized(mirror) {
System.out.println(this.girlName + "获得镜子锁");
Thread.sleep(1000);
synchronized(lipstick) {
System.out.println(this.girlName + "获得口红锁");
}
}
}
}
}
运行结果
Lock锁
从JDK 5.0开始,Java提供了更强大的线程同步机制–通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象 ReentrantLock类实现了Lock ,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的ReentrantL ock,可以显式加锁、释放锁。
synchronized 与 Lock 的对比
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)
- synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序: Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)
不加锁的情况
代码
package ProcessThreadTest;
class MyLock implements Runnable{
int n = 10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(n > 0) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(n--);
}else {
break;
}
}
}
}
public class LockTest {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(myLock).start();
new Thread(myLock).start();
new Thread(myLock).start();
}
}
运行结果
加锁的代码
代码
class MyLock implements Runnable{
int n = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try{
lock.lock();
if(n > 0) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(n--);
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}
结果
线程协作
生产者消费者问题
假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 。
如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到 仓库中的产品被消费者取走为止 。
如果仓库中放有产品 , 则消费者可以将产品取走消费 , 否则停止消费并等待 , 直到仓库中再次放入产品为止 。
这是一个线程同步问题 , 生产者和消费者共享同一个资源 , 并且生产者和消费者之间相互依赖 , 互为条件。对于生产者 , 没有生产产品之前 , 要通知消费者等待 . 而生产了产品之后 , 又 需要马上通知消费者消费。对于消费者 , 在消费之后 , 要通知生产者已经结束消费 , 需要生产新的产品以供消费. 在生产者消费者问题中 , 仅有synchronized是不够的。synchronized 可阻止并发更新同一个共享资源 , 实现了同步 u synchronized 不能用来实现不同线程之间的消息传递 (通信)。
解决线程通信的方法
注意 : 均是Object类的方法 , 都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException
管程法
代码实现
package ProcessThreadTest;
import java.util.Random;
public class ProductorConsumerTest {
public static void main(String[] args) {
SynContainer sc = new SynContainer();
new Productor(sc).start();
new Consumer(sc).start();
}
}
class toy{
private int id;
toy(int i){
setId(i);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
class Productor extends Thread{
SynContainer sc = new SynContainer();
Productor(SynContainer sc){
this.sc = sc;
}
public void run() {
Random random = new Random();
for(int i = 1;i<=100;i++) {
try {
Thread.sleep(random.nextInt(80));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.sc.push(new toy(i));
System.out.println("生产了第"+i+"只鸡"+",剩余"+sc.getNums());
}
}
}
class Consumer extends Thread{
SynContainer sc = new SynContainer();
Consumer(SynContainer sc){
this.sc = sc;
}
public void run() {
Random random = new Random();
for(int i = 0;i<100;i++) {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
toy t = this.sc.pop();
System.out.println("消费了第" +t.getId()+"只鸡"+",剩余"+sc.getNums());
}
}
}
class SynContainer{
private toy[] toys = new toy[10];
private int nums = 0;
public int getNums() {
return nums;
}
public synchronized void push(toy t) {
if(nums >= 10) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
toys[nums] = t;
nums++;
this.notifyAll();
}
public synchronized toy pop() {
if(nums <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
nums--;
toy t = toys[nums];
this.notifyAll();
return t;
}
}
运行结果
信号灯法
代码测试
package ProcessThreadTest;
public class ProductorConsumerTest1 {
public static void main(String[] args) {
TV tv = new TV();
new Watcher(tv).start();
new Player(tv).start();
}
}
class TV{
String program;
boolean flag = true;//true表示开始未表演状态
public synchronized void play(String program) {
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("演员表演了:"+program);
this.notifyAll(); //通知观众来看
this.program = program;
this.flag = !this.flag;
}
public synchronized void watch() {
if(flag) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了"+program);
this.notifyAll();
this.flag = !this.flag;
}
}
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0;i<10;i++) {
this.tv.play("节目"+i);
}
}
}
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0;i<10;i++) {
this.tv.watch();
}
}
}
运行结果
线程池
特点
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。 可以避免频繁创建销毁、实现重复利用。
好处: 提高响应速度;降低资源消耗;便于线程管理。
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0起提供了线程池相关API:ExecutorService
和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行 Callable
void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
测试代码
package ProcessThreadTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.shutdown();
}
}
class MyThread00 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<1;i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
运行结果
学习链接