win2008 iis7发布网站阿里云网站备份
web/
2025/10/8 15:24:18/
文章来源:
win2008 iis7发布网站,阿里云网站备份,哪些网站可以免费做代码,广西网站建设建议多线程
程序、进程、线程的概念
程序#xff1a;是指令和数据的有序集合#xff0c;是一个静态的概念。比如#xff0c;在电脑中#xff0c;打开某个软件#xff0c;就是启动程序。
进程#xff1a;是执行程序的一次执行过程#xff0c;是一个动态的概念#xff0c;…多线程
程序、进程、线程的概念
程序是指令和数据的有序集合是一个静态的概念。比如在电脑中打开某个软件就是启动程序。
进程是执行程序的一次执行过程是一个动态的概念是系统资源的分配单位。
当我们运行一个程序后这个程序的进程就会启动。将来可以在任务管理器中查看进程。
线程一个进程中可以包含若干个线程至少包含一个线程。线程是CPU调度和执行的单位。
在同一个进程中可以执行多个任务每个任务就可以看成是一个线程。
比如放音乐的时候可以一边放音乐一边下载其他音乐
看电影的时候播放器中的画面、声音、弹幕都是独立的线程。
总结
进程可以指运行中的程序特点动态性、独立性、并发性。
线程是进程内部的执行单位它是程序中一个顺序控制流程。 多线程就是一个进程中同时有多个线程用于完成不同的工作。
Java中的线程
主线程、子线程
main()方法就是主线程的入口其中执行的内容就是主线程内容其他的子线程需要通过特殊的类去创建在main()方法中执行main()方法必须最后执行完毕因为它要执行各种关闭操作。
package com.day17.thread1;public class ThreadDemo {public static void main(String[] args) {//测试main()方法中的线程//返回当前main()方法中的线程对象Thread thread Thread.currentThread();//返回当前线程名称System.out.println(thread.getName());//设置main线程的名字thread.setName(主线程);System.out.println(thread.getName());}
}Java中线程的创建和启动
1、继承Thread类
构造方法
Thread()
分配一个新的 Thread对象。
Thread(Runnable target)
分配一个新的 Thread对象。
Thread(Runnable target, String name)
分配一个新的 Thread对象。
Thread(String name)
分配一个新的 Thread对象。 常用普通方法
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
String getName()
返回此线程的名称。
void join()
等待这个线程死亡。
void run()
如果这个线程使用单独的Runnable运行对象构造则调用该Runnable对象的run()方法;
否则此方法不执行任何操作并返回。
void setName(String name)
将此线程的名称更改为等于参数 name 。
void setPriority(int newPriority)
更改此线程的优先级。
static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停暂时停止执行具体取决于系统定时器和调度程序的精度和准确性。
void start()
导致此线程开始执行; Java虚拟机调用此线程的run方法。
static void yield()
对调度程序的一个暗示即当前线程愿意产生当前使用的处理器。
继承Thread类创建多线程的步骤
1、继承Thread类
2、重写run方法将来要其他线程要执行的逻辑内容都放在run方法中
3、实例化子类对象后调用start方法来启动线程
调用start方法的时候是在告诉JVM分配线程去调用run()方法
调用run()方法和start()方法的区别
run方法是继承了Thread类重写的一个方法
当我们调用start()方法的时候JVM会启动一个线程并执行run()方法中的内容
直接调用run()方法会把run()当作main()线程下的一个普通方法执行
不会在某个线程中执行所以并不是多线程工作。
package com.day17.thread1;public class ThreadDemo01 extends Thread{//继承Thread类创建多线程的步骤//1、继承Thread类//2、重写run方法将来要其他线程要执行的逻辑内容都放在run方法中//3、实例化子类对象后调用start方法来启动线程//调用start方法的时候是在告诉JVM分配线程去调用run()方法Overridepublic void run() {for (int i 0; i 10; i) {//获取当前线程名称System.out.println(Thread.currentThread().getName() i);}}public static void main(String[] args) {//main方法既是程序的入口也是一个主线程也是子线程启动的入口ThreadDemo01 thread01 new ThreadDemo01();thread01.setName(子线程1);thread01.start();//启动线程//直接调用run方法JVM默认会将run方法作为普通方法交给main线程来执行// thread01.run();// ThreadDemo01 thread02 new ThreadDemo01();
// thread02.setName(子线程2);
// thread02.start();}
}2、实现Runnable接口
1、实现Runnable接口重写run()方法
2、创建对象把对象作为参数传递到Thread类中中创建Thread对象创建出来的Thread对象才是真正的线程对象
3、通过线程对象调用start()方法
package com.day17.thread2;//1、实现Runnable接口重写run()方法
//2、创建对象把对象作为参数传递到Thread类中中创建
//Thread对象创建出来的Thread对象才是真正的线程对象
//3、通过线程对象调用start()方法
public class ThreadDemo implements Runnable{Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName(): i);}}public static void main(String[] args) {//先创建Runnable接口实现类对象ThreadDemo thread01 new ThreadDemo();// //再通过实现类对象创建Thread对象// Thread t1 new Thread(thread01);// //通过Thread对象调用start()方法// t1.start();// //将创建Runnable接口对象作为参数直接传入Thread类的构造方法// Thread t2 new Thread(new ThreadDemo(), 子线程2);// t2.start();Thread t1 new Thread(thread01, 小红);Thread t2 new Thread(thread01, 小明);t1.start();t2.start();}
}
使用匿名内部类实现Runnable接口实现多线程效果
package com.day17.thread2;
//使用匿名内部类实现Runnable接口实现多线程效果
public class ThreadDemo02 {public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName(): i);}}},小明).start();new Thread(new Runnable() {Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName(): i);}}},小红).start();}
}lambda表达式的写法实现Runnable接口完成多线程实现
是一种函数式编程
语法()-{} ()中将来传递参数{}写重写接口的方法
package com.day17.thread2;
//lambda表达式的写法实现Runnable接口完成多线程实现
//是一种函数式编程
//语法()-{} ()中将来传递参数{}写重写接口的方法
public class ThreadDemo03 {public static void main(String[] args) {new Thread(()-{for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName(): i);}},小红).start();new Thread(()-{for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() : i);}},小明).start();}
}3、实现Callable接口
实现步骤
1.实现Callable接口
2.以实现类为参数创建FutureTask对象
3.将FutureTask对象作为参数创建Thread对象
4.调用start方法
package com.day17.thread7;import java.util.concurrent.Callable;public class CallableDemo implements Callable {Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName()-call方法被执行了);return 10;}
}package com.day17.thread7;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class TestCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建FutureTask对象FutureTaskInteger futureTask new FutureTaskInteger(new CallableDemo());new Thread(futureTask).start();//获取返回call方法的返回值要通过FutureTask对象获取System.out.println(futureTask.get());}
}4、使用Executors工具类创建线程池
package com.day17.thread8;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorsDemo extends Thread{Overridepublic void run() {System.out.println(Thread.currentThread().getName()正在执行...);}public static void main(String[] args) {//通过Executors工具类创建线程池//可变大小的线程池ExecutorService pool Executors.newCachedThreadPool();//固定大小的线程池
// ExecutorService pool Executors.newFixedThreadPool(2);//单个的
// ExecutorService pool Executors.newSingleThreadExecutor();//创建线程对象ExecutorsDemo t1 new ExecutorsDemo();ExecutorsDemo t2 new ExecutorsDemo();ExecutorsDemo t3 new ExecutorsDemo();ExecutorsDemo t4 new ExecutorsDemo();//把线程对象放入池子中pool.execute(t1);pool.execute(t2);pool.execute(t3);pool.execute(t4);//关闭线程池pool.shutdown();}
}package com.day17.thread8;import java.util.Date;public class ExecutorsDemo02 implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName()开始执行new Date());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()结束执行new Date());}
}
package com.day17.thread8;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public final static int CORE_POOL_SIZE 5;public static final int MAX_POOL_SIZE 10;public static final Long KEEP_ALIVE_TIME 1L;//使用阿里巴巴推荐的ThreadPoolExecutor创建线程池public static void main(String[] args) {ThreadPoolExecutor poolExecutor new ThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEP_ALIVE_TIME,TimeUnit.SECONDS,new ArrayBlockingQueue(100));for (int i 0; i 10; i) {ExecutorsDemo02 demo02 new ExecutorsDemo02();poolExecutor.execute(demo02);}//关闭线程池poolExecutor.shutdown();}
}
继承Thread类和实现Runnable接口的使用区别
继承Thread类使用起来比较方便可以直接使用Thread类中的一些方法但是不能实现资源共享。
实现Runnable接口会让编写更具有统一性解决了继承的局限性可以实现线程之间的资源共享。
Runnable和Callable的区别
相同点都是接口都可以编写多线程程序都需要通过Thread.start()方法启动
不同点
1.Runnable接口的run方法没有返回值
Callable接口的call方法有返回值是一个泛型可以结合FutureTask对象获取对象的返回值。
2.Runnable接口的run方法只能抛出运行时异常只能用try/catch
线程执行的生命周期 新生状态new一个线程对象该线程对象就处于新生状态这个时候该线程对象具有自己的内存空间。
就绪状态新生状态的线程对象调用了start方法之后进入就绪状态这时候线程具有运行条件再等待CPU分配时间片。
如果系统选定了一个就绪状态的线程就会从就绪状态进入执行状态这个动作称为CPU调度。
运行状态获取到执行的时间片这时候会执行线程对象中的run方法直到任务完成后死亡或者等待资源阻塞。
阻塞状态运行状态下的线程执行力sleep方法或者io资源阻塞、wait方法、synchronized同步作用下会进入阻塞状态。
阻塞状态下线程不能进入就绪队列只有阻塞原因清除才会重新进入就绪状态中排队等待被系统选中后从原来停止的位置继续执行。
死亡状态线程正常执行完了它的工作、线程被强制终止了、线程抛出未被捕获的异常线程都会进入死亡状态。
package com.day17.thread3;public class ThreadDemo05 implements Runnable{Overridepublic void run() {System.out.println(我正在运行);try {Thread.sleep(2000);//休眠2秒System.out.println(线程休眠后继续运行);} catch (InterruptedException e) {e.printStackTrace();System.out.println(线程被中断);}}public static void main(String[] args) {Thread t new Thread(new ThreadDemo05());System.out.println(线程t进入新生状态1);t.start();System.out.println(线程进入就绪状态2);}
}线程的调度
void join()
等待这个线程死亡。
void setPriority(int newPriority)
更改此线程的优先级。
static void yield()
对调度程序的一个暗示即当前线程愿意产生当前使用的处理器。
package com.day17.thread3;public class ThreadDemo06 implements Runnable{Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() i);}}public static void main(String[] args) {//void setPriority(int newPriority)//更改此线程的优先级并不会一定将线程优先执行完毕Thread t1 new Thread(new ThreadDemo06(), A);Thread t2 new Thread(new ThreadDemo06(), B);// t1.setPriority(Thread.MAX_PRIORITY);
// t2.setPriority(Thread.MIN_PRIORITY);
//
// t1.start();
// t2.start();//void join()//等待这个线程死亡。t1.start();for (int i 1; i 10; i) {if (i5){try {
// t1.join();//插队System.out.println(线程礼让);//线程礼让方法执行之后不代表后面马上会执行其他内容//只是礼让下次执行机会继续再公平竞争Thread.yield();} catch (Exception e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() i);}//static void yield()//对调度程序的一个暗示即当前线程愿意产生当前使用的处理器。}
}void setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。
当运行的唯一线程都是守护进程线程时Java虚拟机将退出。 守护线程运行在后台的一种特殊线程独立于控制终端并且周期性执行某种任务
setDaemon()这个方法必须在启动线程前调用
比如JVM中垃圾回收、内存管理等线程都是守护线程 //1.设为守护线程后主线程结束守护线程也会结束
//2.普通线程主线程结束后普通线程还会继续运行
package com.day17.thread3;public class ThreadDemo07 extends Thread{Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(守护线程);}}public static void main(String[] args) {System.out.println(程序开始);ThreadDemo07 t new ThreadDemo07();//1.设为守护线程后主线程结束守护线程也会结束//2.普通线程主线程结束后普通线程还会继续运行t.setDaemon(true);t.start();//主线程休眠5秒try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(主线程运行结束);}
}线程安全问题
在多线程的运行下程序运行的结果和我们预期的结果不一致排除代码错误的情况
这种情况就可以看作是线程安全问题每次运行的结果可能都会不同问题不易复现
解决比较困难。
package com.day17.thread4;public class T1A {//出现线程安全这个类中必须得有共享资源int num;public int getNum() {return num;}public int updateNum(){return num;}
}
package com.day17.thread4;public class T1 extends Thread{T1A t1a new T1A();Overridepublic void run() {while (true){System.out.println(现在运行的是: Thread.currentThread().getName() num t1a.updateNum());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {T1 t1 new T1();new Thread(t1).start();new Thread(t1).start();new Thread(t1).start();new Thread(t1).start();}
}
通过反编译查看线程安全文件的原因
其实出现线程安全问题的原因就是代码执行的过程并不是原子性的操作一个线程在执行的时候会有其它线程读取到中间步骤或者读取到未修改的值所以导致线程安全问题的出现。 通过synchronized关键字解决线程安全问题。
package com.day17.thread4;import java.util.ArrayList;/*
线程同步示例*/
public class ThreadDemo08 {public static void main(String[] args) {//线程不安全的ListArrayListString list new ArrayList();for (int i 0; i 20000; i) {new Thread(()-{synchronized (list){list.add(Thread.currentThread().getName());}}).start();}for (int i 5; i 0; i--) {try {Thread.sleep(1000);System.out.println(倒计时i);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(list.size());}
}
什么样的情况会发生线程安全问题
1.多线程环境
2.存在共享资源多个线程会去访问共享资源
3.存在并发修改共享资源的情况 线程同步
1、为什么要使用多线程完成并发编程
多线程可以充分利用CPU计算能力通过并发编程的形式可以将多核CPU的计算能力发挥到极致提升性能。 方便进行业务拆分提升系统的并发能力和性能。
2、并发编程的缺点
并发编程虽然能提高效率但是也会产生各种问题比如线程安全、死锁、上下文切换、内存泄露...
3、并发编程三要素【面试】
1.原子性保证程序执行的步骤不可再分要么全部成功要么全部失败。
2.可见性一个线程对共享资源的修改另一个线程可以马上看到。
synchronized、volatile
3. 有序性程序执行的顺序按照代码的先后顺序执行JVM中可能会对指令进行重排序 线程切换可能会带来原子性问题。
缓存导致可见性问题。
编译优化带来有序性问题。
以上三个问题的出现就是线程安全问题的原因。 Java中解决线程安全问题
原子性synchronized、Lock、JDK、Atomic开头的原子类 可以解决原子性问题
可见性synchronized、volatile、Lock 可以解决可见性问题
有序性HappensBefore规则 解决有序性问题
并发相关的概念
并发多个任务在同一个CPU核上按照时间片轮流执行从逻辑上看多个任务是同时执行的。
并行单位时间内多个处理器同时处理多个任务是真正的同时执行。
串行有多个任务由同一个线程按照顺序执行由于任务、方法都在一个线程下执行不存在线程不安全的情况。 高并发同一个对象被多个对象同时操作存在线程安全问题可以通过线程同步来解决并发问题。 线程同步其实就是一种等待机制多个需要同时访问某个对象的线程进入对象的等待池中形成队列前面的线程用完了下个线程再使用。 Java中怎么实现线程同步 Java为了保证数据访问的安全性在多线程中可以加入锁机制synchronized 如果一个线程获取到锁把资源独占其他线程等待前面线程用完之后释放锁。
synchronized关键字的用法
synchronized可以用来控制线程同步在多线程情况下加了这个关键字的代码不会被多个线程执行。
synchronized可以用来修饰类、方法、变量。
早期版本的synchronized比较笨重jdk1.6之后引入了大量的优化。
常用的写法
1、直接写在方法上称为同步方法
public synchronized void method(){}
每个synchronized方法必须获取到调用该方法的对象的锁才能执行否则该线程处于阻塞状态
如果线程拿到锁了就能独占该锁直到方法运行结束释放锁其他的被阻塞的线程才能获取锁。
package com.day17.thread4;/*
初识并发问题模拟抢票案例*/
public class TicketDemo implements Runnable{//设置票数票数是公共的资源多个线程共享private int ticketNumber 10;boolean flagtrue;Overridepublic void run() {while (flag){buyTicket();try {//模拟网络延迟Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}//写一个买票方法public synchronized void buyTicket(){if (ticketNumber 0){flagfalse;return;}// try {// //模拟网络延迟// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }System.out.println(Thread.currentThread().getName()-抢到了第 ticketNumber-- 张票);}public static void main(String[] args) {TicketDemo t new TicketDemo();//把实现类对象传入到Thread对象中创建Thread对象new Thread(t,小明).start();new Thread(t,小红).start();new Thread(t,黄牛).start();}
}
2、修饰静态方法
当synchronized静态方法上的时候就是相当于给类加锁锁的是每个类的Class类对象 会作用于所有对象的实例。
3、使用synchronized修饰代码块成为同步代码块
synchronized(obj){}
同步代码块一般需要传入一个参数这个参数(obj)可以是任意对象将来一般将具有共享资源的对象放在这里作为同步监视器。
package com.day17.thread4;public class TicketDemo02 implements Runnable{//设置票数票数是公共的资源多个线程共享private int ticketNumber 10;boolean flagtrue;Object object new Object();Overridepublic void run() {while (flag){buyTicket();try {//模拟网络延迟Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}//写一个买票方法public void buyTicket(){synchronized (object){if (ticketNumber 0){flagfalse;return;}System.out.println(Thread.currentThread().getName()-抢到了第 ticketNumber-- 张票);}}public static void main(String[] args) {TicketDemo02 t new TicketDemo02();//把实现类对象传入到Thread对象中创建Thread对象new Thread(t,小明).start();new Thread(t,小红).start();new Thread(t,黄牛).start();}
}
synchronized优缺点
优点解决了线程安全问题
缺点性能下降影响效率修饰方法的话每次调用方法就会产生锁浪费资源可能产生死锁 Lock锁
JDK1.5开始提供的一个更强大的线程同步锁
通过显式的定义同步锁对象来实现同步也就是Lock对象。
锁是用于通过多个线程控制对共享资源的访问的工具。
通常锁提供对共享资源的独占访问
一次只能有一个线程可以获取锁并且对共享资源的所有访问都要求首先获取锁。
使用方式
使用Lock接口的常用实现类ReentrantLock
ReentrantLock是一个可重入互斥Lock具有与使用synchronized方法
和语句访问的隐式监视锁相同的基本行为和语义但具有扩展功能
语法
class X {
private final ReentrantLock lock new ReentrantLock();
// ... public void m() {
lock.lock();//手动调用lock方法加锁
try {
// ... method body
}
finally {
lock.unlock() ; //释放锁
}
}
}
package com.day17.thread4;
/*
Lock*/
import java.util.concurrent.locks.ReentrantLock;public class TicketDemo03 implements Runnable{//设置票数票数是公共的资源多个线程共享private int ticketNumber 20;boolean flagtrue;ReentrantLock lock new ReentrantLock();Overridepublic void run() {while (flag){buyTicket();}}//写一个买票方法public void buyTicket(){try {lock.lock();if (ticketNumber 0){flagfalse;return;}System.out.println(Thread.currentThread().getName()-抢到了第 ticketNumber-- 张票);//模拟网络延迟Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();}}public static void main(String[] args) {TicketDemo03 t new TicketDemo03();//把实现类对象传入到Thread对象中创建Thread对象new Thread(t,小明).start();new Thread(t,小红).start();new Thread(t,黄牛).start();}}
Lock和synchronized的区别
1.synchronized 是一个关键字Lock是一个接口ReentrantLock是一个类类中提供了比synchronized更多灵活性的操作比如可以被继承、可以有方法、可以有各种类变量
2.synchronized早期效率比较低jdk1.6之后做了优化后性能较好
3.ReentrantLock使用起来比较灵活但是必须要有开启锁和释放锁的动作synchronized不需要手动开启、释放锁
4.ReentrantLock一般只适用于代码块锁而synchronized可以用于修饰类、变量、方法 Lock和synchronized选择用哪个
因为JDK1.6之后synchronized做了很多优化所以synchronized效率不比Lock而且使用更加简单一般情况下就使用synchronized
死锁
概念
指的是两个或者两个以上的线程中在执行过程中由于竞争资源或者由于彼此通信造成的一种阻塞现象如果没有外力作用他们无法推进下去此时这种状态称为系统的死锁这些永远在互相等待的线程称为死锁线程。
出现死锁后程序不会有异常也不会出现提示只是线程处于阻塞状态无法继续将来编写程序要避免死锁的发生。
出现死锁的条件
1.互斥条件线程对于分配到的资源具有排他性也就是一个资源只能被一个线程占用直到被该线程释放。
2.请求与保持条件一个线程因为请求被占用资源而发生阻塞时对已经获取的资源保持不放。
3.不剥夺条件线程在获取资源后未使用完之前不能被其他线程强行剥夺。
4.循环等待条件当发生死锁的时候所有等待的线程必定会形成一个环路类似于死循环造成永久阻塞。
怎么避免死锁
破坏上面四个条件中的任意一个就不会产生死锁。
package com.day17.deadLock5;//茶杯对象
public class TeaCup {
}package com.day17.deadLock5;//牙膏对象
public class ToothPaste {
}package com.day17.deadLock5;public class BrushTooth implements Runnable{String name;//谁拿了对象int choice;//0茶杯1牙膏static TeaCup teaCup new TeaCup();static ToothPaste toothPaste new ToothPaste();public BrushTooth(String name, int choice) {this.name name;this.choice choice;}Overridepublic void run() {try {brushTooth();} catch (InterruptedException e) {e.printStackTrace();}}//写一个刷牙方法public void brushTooth() throws InterruptedException {//先拿茶杯再拿牙膏if (choice 0){synchronized (teaCup){System.out.println(name拿到了茶杯等待拿牙膏刷牙);Thread.sleep(1000);synchronized (toothPaste){System.out.println(name拿到牙膏开始刷牙);}}}else {//先拿牙膏再拿茶杯synchronized (toothPaste){System.out.println(name拿到了牙膏等待拿茶杯刷牙);Thread.sleep(1000);synchronized (teaCup){System.out.println(name拿到茶杯开始刷牙);}}}}
}package com.day17.deadLock5;public class Test {public static void main(String[] args) {BrushTooth t1 new BrushTooth(A, 0);BrushTooth t2 new BrushTooth(B, 1);new Thread(t1).start();new Thread(t2).start();}
}volatile关键字
Java提供了volatile关键字用来解决多线程中的部分问题。
volatile可以用来保证可见性和有序性不能保证原子性还会出现线程安全问题。
package com.day17.thread6;public class ThreadDemo09 {public int num0;public boolean flag false;//不加volatile程序一直在运行//volatile保证了类属性在多个线程中的可见性//public volatile boolean flag false;//synchronized也可以保证可见性//通过给方法修饰后间接控制属性可见性public synchronized boolean isFlag() {return flag;}public synchronized void setFlag(boolean flag) {this.flag flag;}public synchronized void addNum(){num;}public int getNum() {return num;}public static void main(String[] args) {ThreadDemo09 t1 new ThreadDemo09();new Thread(()-{for (int i 0; i 5; i) {t1.addNum();System.out.println(addNum方法被调用 i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}//将flag设为true// t1.flagtrue;t1.setFlag(true);System.out.println(已经将flag设为true了。);}).start();new Thread(()-{System.out.println(线程2正在执行);// while (!t1.flag){// }while (!t1.isFlag()){}System.out.println(num的值是t1.getNum());}).start();}
}
package com.day17.thread6;public class ThreadDemo10 {//volatile不能保证原子性public volatile int num0;
// public int num0;// public synchronized void addNum(){public void addNum(){num;}public int getNum() {return num;}public static void main(String[] args) {ThreadDemo10 t1 new ThreadDemo10();//循环10次每次创建一个子线程每个子线程做了100次 num//如果有原子性的线程安全的最终的值应该是1000//在属性前加上volatile不能保证原子性所以最终的值不是1000//使用synchronized关键字加在方法前面可以保证原子性for (int i 0; i 10; i) {new Thread(()-{for (int j 0; j 100; j) {t1.addNum();try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}//获取值for (int i 0; i 10; i) {System.out.println(num的值是t1.getNum());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}synchronized和volatile的区别
synchronized表示只有一个线程可以获取作用对象的值执行代码会阻塞其他线程。
volatile表示变量在CPU的寄存器中是不确定的必须从内存中读取保证多线程下变量的可见性
禁止指令重排有序性。
区别
1.volatile是用来修饰变量的synchronized可以修饰类、方法、变量。
2.volatile只能实现变量的可见性不能保证原子性synchronized可以保证原子性和可见性。
3. volatile不会造成线程阻塞synchronized会造成线程阻塞。
4.volatile标记的变量不会被编译器优化synchronized标记的变量可以被编译器优化。
5.volatile性能比synchronized要好。
线程交互
Object类中的方法 wait()、notify()、notifyAll()
wait()方法释放占有的对象锁线程进入等待池释放CPU
其他正在等待的线程可以拿到锁获取到锁的线程可以执行程序。
notify()方法该方法会唤醒因为调用wait()方法而等待的线程其实就是对 对象锁 的唤醒
被唤醒的线程可以有机会继续获得对象锁从而执行程序。
另一个线程调用notify()后不会马上释放锁继续把代码执行完毕才会释放锁。
notifyAll()方法所有线程 void notify()
唤醒正在等待对象监视器的单个线程。
void notifyAll()
唤醒正在等待对象监视器的所有线程。
void wait()
导致当前线程等待直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。 注意wait()和notify()都需要synchronized中调用。
因为wait()就是用来释放锁的线程必须拥有该对象的锁才能去释放锁。
package com.day17.thread9;public class WaitDemo01 implements Runnable{int count0;Overridepublic void run() {while (count10){synchronized (ThreadWait.obj){//线程第一次进来没有线程等待不需要执行唤醒if (count!0){javaThreadWait.obj.notify();}System.out.println(Acount);try {ThreadWait.obj.wait();} catch (InterruptedException e) {e.printStackTrace();}count;}}}
}
package com.day17.thread9;public class WaitDemo02 implements Runnable{int count0;Overridepublic void run() {while (count10){synchronized (ThreadWait.obj){//唤醒线程1唤醒之后会继续执行当前代码//代码执行完才会释放锁ThreadWait.obj.notify();System.out.println(Bcount);//当程序运行到9表示循环结束了不需要再等待了if (count ! 9){try {ThreadWait.obj.wait();} catch (InterruptedException e) {e.printStackTrace();}}count;}}}
}
package com.day17.thread9;public class ThreadWait {//创建一个对象public static final Object obj new Object();public static void main(String[] args) {new Thread(new WaitDemo01()).start();new Thread(new WaitDemo02()).start();}
}
【面试题】wait()和sleep()的区别
相同点都可以让线程暂停进入阻塞状态。
不同点
1.所属类不同wait()方法属于Object类的一个方法sleep()是属于Thread类的一个方法。
2.锁释放不同wait()会释放锁sleep()不会。
3.用途不同wait()常被用于线程间的交互通信sleep()一般用来暂停线程的执行。
4.用法不同wait()方法被调用线程不会自动苏醒需要别的线程调用同一个对象上的notify()方法或者notifyAll()方法进行唤醒操作如果调用wait(long timeout) 超时后会自动唤醒。
sleep()执行完之后线程会自动苏醒。
生产消费者模型基于wait方法的一个应用模型
不是面向对象的设计模式
也可以称为生产者 - 消费者 - 仓库模型
1.生产者在仓库未存满的时候开始生产商品仓库满了停止生产
2.消费者在仓库有产品的时候才能消费仓库空了则等待
3.当消费者发现仓库没有产品会通知生产者生产
4.当生产者生产可以消费的产品则通知消费者消费。
这个模型有哪些类
生产者就有生产方法
消费者具有消费方法
仓库添加产品减少产品
产品
package com.day17.thread10;
//炸鸡产品
public class Chicken {int number;public Chicken(int number) {this.number number;}}
package com.day17.thread10;
//仓库
public class Warehouse {//表示仓库中最多可以被存放多少个产品Chicken[] chickens new Chicken[10];//计数变量用来记录产品数量int num0;//取出产品public synchronized Chicken pop() {//如果仓库中没有产品消费者等待while (num0){try {this.wait();//消费者等待} catch (InterruptedException e) {e.printStackTrace();}}num--;try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}Chicken chicken chickens[num];this.notifyAll();return chicken;}//添加产品public synchronized void put(Chicken chicken) {//如果产品满了不生产等待消费while (num chickens.length-1){try {this.wait();//停止生产等待消费} catch (InterruptedException e) {e.printStackTrace();}}//产品没满就通知生产者生产chickens[num] chicken;num;this.notifyAll();}}
package com.day17.thread10;
//生产者
public class Producer implements Runnable{//声明一个仓库对象通过仓库对象调用方法Warehouse warehouse new Warehouse();public Producer(Warehouse warehouse) {this.warehouse warehouse;}//生产方法Overridepublic void run() {for (int i 1; i 50; i) {//生产产品其实是在调用仓库对象往仓库中添加产品warehouse.put(new Chicken(i));System.out.println(生产者生产了第 i 号炸鸡);}}}
package com.day17.thread10;
//消费者
public class Consumer implements Runnable{//声明一个仓库对象通过仓库对象调用方法Warehouse warehouse new Warehouse();public Consumer(Warehouse warehouse) {this.warehouse warehouse;}//取出方法Overridepublic void run() {for (int i 1; i 50; i) {//消费产品其实是在调用仓库对象从仓库中取出产品Chicken chicken warehouse.pop();System.out.println(消费者消费了了第 chicken.number 号炸鸡);}}
} package com.day17.thread10;public class Test {public static void main(String[] args) {Warehouse warehouse new Warehouse();new Thread(new Producer(warehouse)).start();new Thread(new Consumer(warehouse)).start();}
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/89123.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!