线程学习
1、run()和start()
2、进程和线程的关系
程序是指令和数据的集合,是一个静态的概念
进程就是跑起来的程序,进程是系统分配资源的单位。一个进程中至少有一个线程
线程是cpu调度和指定的最小单位
3、Thread和Runnable
1、Thread创建线程
-
继承Thread类
-
重写run()方法
-
new 一个线程对象,调用start()方法启动线程
-
public class ThreadStep1 extends Thread{ //创建Thread类 @Override public void run() { System.out.println("this is a run test"); } public static void main(String[] args) { new ThreadStep1().start(); System.out.println("this is main sout"); } }
2、Runnable接口
-
实现Runnable接口
-
重写run()方法
-
将线程丢入Thread初始化
-
使用Thread.strat()启动
public class RunnableStep2 implements Runnable{ @Override public void run() { System.out.println("runnable 方法"); } public static void main(String[] args) { new Thread(new RunnableStep2()).start(); System.out.println("this is main"); } }
3、抢票例子
线程不安全
public class ThreadStep4 implements Runnable{
//票数
private int ticketNum = 10;
@Override
public void run() {
while(true){
if (ticketNum<=0)
break;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNum--+"票");
}
}
public static void main(String[] args) {
ThreadStep4 step4 = new ThreadStep4();
new Thread(step4,"a").start();
new Thread(step4,"b").start();
new Thread(step4,"c").start();
}
}
a-->拿到了第10票
c-->拿到了第8票
b-->拿到了第9票
b-->拿到了第6票
a-->拿到了第5票
c-->拿到了第7票
b-->拿到了第4票
c-->拿到了第3票
a-->拿到了第4票
b-->拿到了第2票
a-->拿到了第1票
c-->拿到了第0票
4、Callable接口
E:\Idea_workspace\多线程\learnDemo1\src\main\java\demo02\CallableStep5.java
- 实现Callable接口,需要返回值接口
- 重写call方法,需要抛出异常
5、静态代理模式
真实对象和代理对象实现同一个接口
对于线程中来说,
Marry就是Thread类
HappyMarry就是run方法
package demo03_proxy;
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany company = new WeddingCompany(new You());
company.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("你要结婚了,新郎不是我");
}
}
class WeddingCompany implements Marry{
//真实角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry() {
before();//代理对象帮真实对象做
this.target.HappyMarry();//真实对象
after();
}
private void after() {
System.out.println("结婚后");
}
private void before() {
System.out.println("结婚前");
}
}
4、Lambda表达式
E:\Idea_workspace\多线程\learnDemo1\src\main\java\demo04_lambda
适合用于匿名类
相当于实现类接口中唯一的方法
Runnable接口只有一个run方法,因此lambda可以用于Runnable接口的实现
(params)->expression[表达式]
(params)->statement [语句]
(params)->{statements}
函数式接口
- 任何一个接口,如果只包含唯一一个抽象方法,那他就是一个函数式接口
public interface Runable{
public abstract void run(){};
}
- 对于函数式接口,可以通过lambda表达式来创建该接口的对象
演变
实现类->内部类->局部内部类->匿名类->lambda表达式
package demo04_lambda;
public class LambdaStep5 {
//3、静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4、局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda3");
}
}
like = new Like3();
like.lambda();
//5、匿名内部类
like = new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6、用lambda简化
like = ()->{
System.out.println("i like lambda5");
};
like.lambda();
}
}
//1、接口
interface ILike{
void lambda();
}
//2、实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda1");
}
}
使用
package demo04_lambda;
public class LambdaTest {
public static void main(String[] args) {
ILove love;
//1.
love = (String name)->{
System.out.println("i love "+name);
};
love.love("1");
//2.去掉参数类型
love = (name)->{
System.out.println("i love "+name);
};
love.love("2");
//3、去掉括号
love = name -> System.out.println("i love "+name);
love.love("钝角");
}
}
interface ILove{
void love(String name);
}
Lambda和Runnable
package demo04_lambda;
public class LambdaRunnable {
public static void main(String[] args) {
Runnable runnable = ()->{
System.out.println("this is a runnable+lambda test");
};
new Thread(runnable).start();
}
}
5、线程方法
1、线程五个状态
2、线程方法
3、停止线程
不建议使用stop 或destroy
- 程序正常停止----设置执行次数
- 使用标志位------设置flag标志
在run方法中使用while(flag)包含函数体,使得线程的停止可以由flag操纵,
创建一个方法控制线程的停止
public void run() {
int i = 0;
while(flag){
System.out.println("run.....Thread"+i++);
}
}
public void stop(){
this.flag=false;
}
4 线程休眠
sleep(time) time的单位是毫秒
sleep 存在异常 InterruptedException
sleep 时间到达后线程进入就绪状态
sleep 可以模拟网络延时,倒计时
每一个对象都有一个锁, sleep不会释放锁
5 线程礼让 Yield
Thread.yield()
停止当前线程,转为就绪状态,重新由cpu选择运行
6 线程 join
停止其他线程,先执行完此线程
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("join "+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin join = new TestJoin();
Thread thread = new Thread(join);
thread.start();
for (int i = 0; i < 500; i++) {
if (i == 200){
thread.join();
}
System.out.println("main "+i);
}
}
}
在thread.join()之前,main和thread同时执行,之后在thread执行完后,继续执行main
7 线程状态 Thread.state
-
public static enum Thread.State extends Enum<Thread.State>
线程状态。线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。RUNNABLE
在Java虚拟机中执行的线程处于此状态。BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
Thread.State state = thread.getState();
通过getState()获得线程当前状态
8 线程优先级 Priority
先设置优先级,然后start()
设置的只是优先调动的概率,不能一定按这个顺序
-
public static final int
MAX_PRIORITY
10
public static final int
MIN_PRIORITY
1
public static final int
NORM_PRIORITY
5
获取和改变优先级,范围1~10
getPriority()
setPriority(int x)
9 线程守护 daemon
setDaemon(boolean on)
默认是false,表示用户线程
true表示守护线程,在所有用户线程结束后自动结束
如下,you线程执行完毕后,god线程也会结束,不会一直执行
package demo09_ThreadDaemon;
public class ThreadDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认false-用户线程
//守护线程和程序同一时间结束
thread.start();
new Thread(you).start();
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("happy live");
}
System.out.println("hello god!");
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("god bless you");
}
}
}
6、线程同步
1、并发
并发:同一个对象被多个线程同时操作
多个线程访问时,用到线程池
2、队列和锁
队列和锁保证线程安全
synchronized
会损失性能
3、同步
-
同步方法
public synchronized void method(int args){}
默认锁this
-
同步块
synchronized(obj){}
其中obj是需要锁的对象
synchronized (account){
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+" 余额不足");
}
else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingMoney;
money = money + drawingMoney;
System.out.println(account.name+" 余额为: "+ account.money);
System.out.println(this.getName()+" 手中钱:"+ money);
}
}
4、死锁
多个对象互相持有对方需要的资源
造成死锁
获得一个资源后不释放,再想获得另一个资源
死锁例子(嵌套synchronized)
synchronized (lipstick){
System.out.println(this.girlName+" sync of lipstick");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.girlName+" sync of mirror");
}
}
5、Lock锁
Lock是显示锁 需要手动开关,synchronized是隐式锁,处理作用域自动释放
//定义可重复Lock锁 private final ReentrantLock lock = new ReentrantLock();
class TestLock implements Runnable{
int tickeNum = 1000;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock();//加锁
try {
if (tickeNum>=0){
System.out.println("ticke: "+tickeNum--);
} else {
break;
}
}finally {
lock.unlock();
}
}
}
}
7、线程通信
方法 说明 wait() 表示线程一直等待,直到接到通知(会释放锁) wait(long timeout) 指定线程等待的毫秒数 notify() 唤醒一个处于等待状态的线程 notifyAll() 唤醒同一个对象上的所有处于等待状态的线程
上述方法只能在同步块中使用
1、管程法
生产者消费者问题
package demo10_sync;
//管程法:线程通信
//生产者、消费者、产品、缓存区
public class ProducerConsumer {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
class Producer extends Thread{
SynContainer synContainer;
public Producer(SynContainer synContainer){
this.synContainer = synContainer;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了第:"+i+"鸡");
synContainer.push(new Product(i));
System.out.println("当前余量:"+(10-synContainer.count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
SynContainer synContainer;
public Consumer(SynContainer synContainer){
this.synContainer = synContainer;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->:"+synContainer.pop().id+"鸡");
System.out.println("当前余量:"+(10-synContainer.count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Product{
int id;
public Product(int i){
this.id = i;
}
}
class SynContainer{
//容器大小
Product[] products = new Product[10];
//计数器
int count=0;
//生产者放入
public synchronized void push(Product product){
//容器满
while(count>=10) {
//通知消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//产品放入容器
products[count] = product;
count++;
//通知可以消费
this.notifyAll();
}
//消费者消费
public synchronized Product pop(){
//容器为空
while (count<=0){
//消费者等待,生产者生产
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//消费者消费
count--;
Product product = products[count];
//通知可以生产
this.notifyAll();
return product;
}
}
2、信号灯法 标志位
设置标志位,通过判断标志位进行等待或其他
package demo10_sync;
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new TVShow(tv).start();
new Audience(tv).start();
}
}
//节目
class TVShow extends Thread{
TV tv;
public TVShow(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
tv.player("快本");
//模拟播放
}
else {
tv.player("天天向上");
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//观众
class Audience extends Thread{
TV tv;
public Audience(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("看完了-->"+tv.showing);
tv.watch();
}
}
}
//电视机
class TV{
//两种状态:看节目 节目,观众看;广告,观众等
boolean flag = true;
//电视正在播放的
String showing;
public synchronized void player(String show){
if (!flag){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
this.showing = show;
this.flag = !this.flag;
System.out.println("正在播放:"+show+"...");
//节目切换,通知观看
this.notifyAll();
}
public synchronized void watch(){
if (flag){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//观众看完
this.flag = !this.flag;
//通知换节目
this.notifyAll();
}
}
3、线程池
1、新建一个线程池
ExecutorService service = Executors.newFixedThreadPool(10);
public static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 在任何时候,最多
nThreads
线程将处于主动处理任务。 如果所有线程处于活动状态时都会提交其他任务,则它们将等待队列中直到线程可用。 如果任何线程由于在关闭之前的执行期间发生故障而终止,则如果需要执行后续任务,则新线程将占用它。 池中的线程将存在,直到它明确地为shutdown
参数
nThreads
- 池中的线程数结果
新创建的线程池
异常
IllegalArgumentException
- 如果是nThreads <= 0
2、执行
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
3、 关闭
service.shutdown();