多线程部分案例总结

来源:互联网 发布:怎么读懂易语言源码 编辑:程序博客网 时间:2024/06/06 13:18
多线程部分案例总结


1.关于进程和线程的理解
进程:正在执行的应用程序
线程:进程的执行单元,执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径
多进程的意义:在于一高CPU的使用率
多进程的意义:在于提高应用程序的执行率

2.Java程序的运行原理
有Java命令启动JVM,JVM启动则相当于启动了一个进程
之后由该进程创建一个主线程去调用main方法

3.JVM的启动是否是多线程?
为防止出现内存溢出的情况,JVM启动的时候会同时启动垃圾回收线程
加上主线程,则JVM启动时最少有两个线程启动,由此可知JVM启动时多线程。

4.run()和start()的区别
run():仅仅是封装了背线程执行的代码,直接调用时普通方法。
start():首先启动了线程,然后再由JVM去调用该线程的run()方法。

5.多线程的实现方式一:继承Thread类,重写run()方法
A:常用功能
public final String getName():获取线程的名称
public final void setName(String name):设置线程的名称(也可用带参构造方法为线程设置名称)
public static Thread currentThread():返回当前正在执行的线程对象
Thread.currentThread().getName():返回当前正在执行的线程对象的名称
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程
注意:当正在运行的线程都是守护线程时,Java虚拟机退出。
public final void join():等待该线程终止
public final int getPriority():返回线程对象的优先级
public final void setPriority(int newPriority):设置线程的优先级
注意:线程优先级的范围是1-10,默认为5.线程优先级高仅仅代表线程获取的CPU时间片的几率高
public static void sleep(long millis):线程休眠
public final void stop():线程停止,该方法已过时,但仍然可以使用
public void interrupt():中断线程,终止线程状态,并抛出一个InterruptedException异常
public static void yield():暂停当前正在执行的线程对象,并执行其他线程
注意:该方法可以让线程执行更加和谐,但无法保证一人一次的执行。

B:电影院三窗口售票案例方式一实现
public class SellTicket extends Thread{
private static int tickets = 100;
public void run(){
while(true){
if(tickets > 0){
System.out.println(getName() + "正在出售第" + (tickets--) + "张票")
}
}
}
}

public class SellTicketDemo{
public static void main(String[] args){
SellTicket st1 = new SellTicket();
SellTicket st1 = new SellTicket();
SellTicket st1 = new SellTicket();
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
st1.start();
st2.start();
st3.start();
}
}


6.多线程的实现方法二:实现Runnable接口,重写run()方法。
A:电影院三窗口售票案例方式二实现
public class SellTicket implements Runnable{
private int tickets = 100;
public void run(){
while(true){
if(tickets > 0){
System.out.println(Thread.currentThread().getName() + "正在出售第" 
+ (tickets--) + "张票");
}
}
}
}

public class SellTicketDemo{
public static void main(String[] args){
SellTicket st = new SellTicket();
Thread t1 = new Thread(st,"窗口1");
Thread t2 = new Thread(st,"窗口2");
Thread t3 = new Thread(st,"窗口3");
t1.start();
t2.start();
t3.start();

}
}


7.线程生命周期
A:新建
B:就绪
C:运行
D:阻塞
E:死亡

8.线程安全问题
A:判断多线程是否会出现安全问题的依据
a:是否是多线程环境
b:是否有共享数据
c:是否有多天语句操作共享数据


B:在设置了线程休眠之后,出现了同票和负票问题。
同票原因:CPU的每一次执行是一个原子性(最简单的)操作。
负票原因:线程随机性和延迟导致了负票。

9.加锁解决线程安全问题
同步的好处:解决了线程安全问题
同步的弊端:每个线程都需要判断同步上锁的状态,降低了运行效率且容易产生死锁

A:同步代码块(锁对象可以为任意对象)
public class SellTicket implements Runnable{
private int tickets = 100;
private Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tickets > 0){
try{
Thread.sleep(1000);
}chtch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName + 
"正在出售第" + (tickets--) + "张票");
}
}
}
}
}

B:同步方法(锁对象时this)
public class SellTicket implements Runnable{
private int tickets = 100;

public void run(){
while(true){
sellTicket();
}
}
public synchronized void sellTicket(){
if(tickets > 0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
"正在出售第" + (tickets--) + "张票");
}
}
}

C:静态同步方法(锁对象时当前类的字节码文件对象)
与同步方法不同的地方在于:方法申明中多了静态修饰符:static
public static synchronized void sellTicket(){}

10. 使用锁对象Lock解决线程安全问题
A:ReentrantLock是Lock的实现类
B:常用方法
void lock():获取锁
void unlock():释放锁

C:解决电影票问题
public class SellTicket() implements Runnable{
private int stckets = 100;
private Lock lock = new ReenTrantLock();


public void run(){
while(true){
try{
lock.lock();
if(tickets > 0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
"正在出售第" + (tickets--) + "张票");
}
}finally{
lock.unlock();
}
}

}

}

11.死锁问题
A:死锁
两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象
B:代码演示
public class MyLock{
//创建两个锁对象
public static final Object objA = new Object();
public static final Object objA = new Object();
}

public class DieLock extends Thread{
private boolean flag;
public DieLock(boolean flag){
this.flag = flag;
}

public void run(){
if(flag){
synchronized(MyLock.objA){
System.out.println("if objA");
synchornized(MyLock.objB){
System.out.println("if objB");
}
}
}else{
synchornized(MyLock.objB){
System.out.println("else objB");
synchornized(Mylock.objA){
System.out.println("else objA");
}
}
}
}
}

public class DieLockDemo{
public static void main(String[] args){
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);

dl1.start();
dl2.start();
}
}

12.线程间通信问题
A:等待唤醒(使用Object类中提供的三个方法)
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
注意:以上三个方法需要通过锁对象调用,而锁对象可以是任意对象,所以这三个方法必须定义在Object类中

B:以生产者和消费者多线程体现
资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo

public class Student {
String name;
int age;
boolean flag;
}

public class SetThread implements Runnable{
private Student s;
private int x = 0;
public SetThread(Student s){
this.s = s;
}

public void run(){
while(true){
synchronized(s){
if(s.flag){
try{
s.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
if(x%2 == 0){
s.name = "高渐离";
s.age = 30;
}else{
s.name = "雪女";
s.age = 28;
}
x++;
s.flag = true;
s.notify();
}
}
}
}

public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}

public void run(){
while(true){
synchronized(s){
if(!s.flag){
try{
s.wait();
}catch(InterruptedException e){
e.printStackTrace()
}
}
System.out.println(s.name + " --- " + s.age);
s.flag = false;
s.notify();
}
}
}
}

public class StudentDemo {
public static void main(String[] args){
//创建资源对象
Sudent s = new Student();
//创建设置和获取对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
//创建设置和获取线程
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}

13.线程组
A:常用功能
ThreadGroup(String name):通过该构造方法创建线程组
Thread(ThreadGroup group,Runnable target,String name):将线程添加到线程组,并为线程组命名
public final ThreadGroup getThreadGroup():获取该线程所属线程组
public final String getName():返回该线程组名称
Thread.currentThread().getThreadGroup().getName():获取正在运行的线程所属线程组的名称

B:代码演示
public class MyRunnable implements Runnable{
public void run(){
for(int x = 0; x < 100; x++){
System.out.println(Thread.currentThread().getName() +  ":" + x);
}
}
}

public class ThreadGroupDemo{
public static void main(String[] args){
MyRunnable mr = new MyRunnable();
ThreadGroup tg = new ThreadGroup("线程组一");

Thread t1 = new Thread(tg,mr,"线程A");
Thread t2 = new Thread(tg,mr,"线程B");

System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
//通过线程组名称设置改组线程为后台线程
tg.setDaemon(true);

t1.start();
t2.start();
}
}

14.线程池
线程池的好处:
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待先一个对象来使用

A:常用方法
创建线程池
public static ExecutorService newFixedThreadPool(int nThreads)
调用方法
Future<?> submit(Runnable task):执行Runnable对象
<T> Future<T> submit(Callable<T> task):执行Collable对象
结束线程池
void shutdown();
注意:Future<V> :表示计算结果,使用 V get() 方法获取结果
 Collable 重写的是 call() 方法,该方法有返回值,返回值类型为Callable<T>泛型类型
 若没有指定泛型类型,则默认使用Object类型
 
B:多线程的线程池实现方式
a:实现Runnable接口
public class MyRunnable implements Runnable {
public void run(){
for(int x = 0;x < 100;x++){
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}

public class ExecutorsDemo{
public static void main(String[] args){
//创建线程池,设置要创建的线程对象的数量
ExecutorService pool = Executors.newFixedThreadPool(2);
//执行Runnable对象代表的线程
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//结束线程
pool.shutdown();
}
}

b:实现Callable<T>接口(多线程求和案例)
public class MyCollable implements Callable<Integer> {
private int number;
public MyCollable(int number){
this.number = number;
}

public Integer call() throws Exception {
int sum = 0;
for(int x = 1; x <= number; x++){
sum += x;
}
return sum;
}
}

public class CallableDemo{
public static void main(String[] args) throws InterruptedException, ExecutionException{
//创建线程池对象,设置数量
ExecutorService pool = Executors.newFixedThreadPool(2);
//执行Collable<T>对象代表的线程
Future<Integer> f1 = pool.submit(new MyCollable(100));
Future<Integer> f2 = pool.submit(new MyCollable(200));
//使用V get() 方法获取计算结果
Integer i1 = f1.get();
Integer i2 = f2.get();
//结束线程池
pool.shutdown();
}
}

15.多线程的匿名内部类实现方式
public class ThreadDemo{
public static void main(String[] args){
//基础Thread类实现多线程(使用子类对象)
new Thread(){
public void run(){
for(int x = 0; x < 100; x++){
System.our.println(Tread.currentThread().getName() + ":" +x)
}
}
}.start();

//实现Runnable接口来实现多线程(使用匿名内部类)
new Thred(new Runnable(){
public void run(){
for(int x = 0; x < 100; x++){
System.out.println(Thread.currentThread().getName() + ":" +x);
}
}

}){}.start();

}
}
注意:若同时子类对象和匿名内部类都为线程,不会报错,但只执行子类对象线程

16. 定时器
定时器:可以实现在指定的时间内做某件事,还可以实现重复的做某件事
定时器依赖Timer和TimerTask这两个类
Timer:定时
TimerTask:任务

A:常用功能
punlic Timer():创建定时器对象的构造方法
public void schedule(TimerTask task,long dalay):指定多长时间后执行任务
public void schedule(TimerTask task,long delay,long period):指定多长时间后执行任务,指定多长时间后再次执行任务
public void cancel():取消计时器任务

B:案例演示
public class TimerDemo{
public static void main(String[] args){
//创建定时器对象
Timer t = new Timer();
//执行任务
t.schedule(new MyTask(),3000,2000);
//结束任务
t.cancel()
}
}

public class MyTask extends TimerTask{
public void run(){
System.out.println("任务已执行!");
}
}

C:在指定的时间内删除指定的目录
public class TimerTest{
public static void main(String[] args) throws ParseException {
Timer t = new Timer();

String str = "2015-12-12 23:59:59";
SimpleDeteFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);

t.schedule(new DeleteFolder(),d);
}
}

public class DeleteFolder extends TimerTask{
public void run(){
File srcFolder = new File("demo");
deleteFolder(srcForder);
}
//递归删除目录
public void deleteFolder(File srcFolder){
File[] fileArray = srcFolder.listFiles();
if(fileArray != null){
for(File file : fileArray){
if(file.isDirectory()){
deleteFolder(file);
}else{
System.out.println(file.getName()+ ":" +file.delete());
}
}
System.out.println(srcFolder.getName()+ ":" +srcFolder.delete());
}
}
}
0 0