java+多线程哪些配置参数_Java基础--多线程的方方面面

1,什么是线程?线程和进程的区别是什么?

线程是程序执行的最小单元。

区别: 进程是操作系统进行资源处理和分配的最小单位,而一个进程可以包含多个线程,并共享进程的资源。

2,什么是多线程?为什么设计多线程?

介绍之前,我们需要理解并行和并发的定义:

并行:同一个时刻有多个线程进行。

并发:同一个时间段内有多个线程进行。

多线程指的是一个进程可以包含多个并发的线程(同一个时刻只有一个线程运行)。例如酷狗,我们可以一边听歌一边搜索自己喜欢的歌曲。多线程的存在能够让进程及时处理我们多项的请求,提高应用程序的利用率。

多线程编程需要了解到多线程运行面临的问题。

既然一个进程的多个线程共享进程的资源,怎样保证有多个线程访问同一资源时单个线程的访问不受其它线程的干扰。这是线程安全问题。

多线程怎么控制线程的执行顺序,这是线程调度问题。ps:Java对多线程调度执行抢占式,每个线程有个优先级属性(1--10,10最高),优先级高的有限执行。

3,Java种多线程的实现方式是什么?有什么区别?

Java实现多线程有两个方式。

继承Thread类,重写run()方法,代码如下:

线程类MyThread:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public class MyThread extendsThread{

@Overridepublic voidrun() {for(int x=0;x<200;x++){

System.out.println(x);

}

}

}

View Code

主类Demo:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public classDemo {public static voidmain(String[] args) {

MyThread mt= new MyThread();//新建线程类对象

MyThread mt1 = newMyThread();

mt.start();//调用start()方法

mt1.start();

}

}

View Code

ps,为什么不直接调用线程类的run()方法,而调用start()方法?

run()方法只是封装了多线程执行的操作,只是一个普通方法。

start()方法是启动线程执行的方法,由JVM自动调用run()方法。

实现Runnable 接口,重写run()方法,重点是主类调用的时候不同。

步骤:1,编写实现Runnable 接口的类,重写run()方法

public class ThreadRunnable implementsRunnable { public voidrun(){}}

2,在主类中新建线程类对象,obj

ThreadRunnable tr = new ThreadRunnable();

3,新建Thread类t,将obj作为t的构造参数

Thread t = new Thread(tr);

4,调用t的start()方法。

继承类Thread和实现Runnable接口对比

由于Java只允许单类继承,故多选用实现Runnable接口的方法创建多线程,事实上Thread类也是接口Runnable的实现类。

public class Threadextends Object implements Runnable

4,线程的状态控制有哪些方法?

线程状态控制常用到的方法如下:

线程睡眠sleep(long millis)

t.sleep(1000);让线程t睡眠1000毫秒,即1秒。

线程加入join()

A.start();

A.join();//try catch

B.start();

C.start();

A执行完之后B和C才可以执行

线程礼让static void yield()

A.yield();A暂停一下,时间不确定,让同等级的线程优先运行。

线程中断interrupt()

A.interrupt();把线程的状态中止,并抛出 InterruptedException。跳出阻塞的部分可以继续执行接下来的代码。

interrupt()只是改变中断状态而已. interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,给受阻塞的线程抛出一个中断信号,这样受阻线程就得以退出阻塞的状态。更确切 的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞, 它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。

如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到InterruptedException异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。

ps:对于一个正在运行的线程如果想要其结束运行,可以使用标志位,让线程跳出从而结束运行,示例如下:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public class ThreadFlag extendsThread

{public volatile boolean exit = false;public voidrun()

{while (!exit);

}

}

View Code

线程等待唤醒wait() notify()。这两个方法并不是线程类的方法,而是锁的方法,在接下一节介绍。

5,线程安全、死锁和生产者--消费者

我们来看下面的一段代码:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public class ThreadRunnable implementsRunnable {private static int D = 100;//D是静态变量由多个线程共享。public void run() {

while(true){if(D>0){try{

Thread.currentThread().sleep(100);

}catch(InterruptedException e1) {

e1.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"---"+(D--));

}

}

}//run

}

View Code

由于D是静态变量,它由ThreadRunnable的所有对象访问,每个对象对它进行输出,并减1的操作,直到D为0(可将D假想成某直达列车的票,每  个ThreadRunnable对象是一个售票窗口)假设有三个窗口如下:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public classThreadRunnableDemo {public static voidmain(String[] args) {

ThreadRunnable tr= new ThreadRunnable();//创建接口对象

Thread t1 = new Thread(tr,"窗口1");//创建Thread类,将上述对象作为构造参数

Thread t2 = new Thread(tr,"窗口2");//创建Thread类,将上述对象作为构造参数

Thread t3 = new Thread(tr,"窗口3");//创建Thread类,将上述对象作为构造参数

t1.start();//启动start方法

t2.start();

t3.start();

}

}

View Code

输出:

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

....

窗口1---97窗口2---97....

....

窗口3---2窗口1---1窗口2---0窗口3----1

View Code

出同号票的原因分析:

57fac591a06b0edd5f9741161b1a8886.png

出现0号和负号票的原因分析:

08f2d26675d68074afc35a82a81ac3b6.png

这就产生了线程不安全的问题,产生线程不安全的场景:

多个线程访问同一资源,并对资源进行多条语句操作就有可能引发线程不安全。

概括:多个线程;同一资源;不是原子操作

前两个条件我们无法改变,我们有的解决思路就是将线程对资源操作语句封装成原子操作(不会被打断)。将操作封装成原子操作。

Java使用synchronized关键字。

使用规范:

对共享代码块进行锁:synchronized(锁对象){共享代码块} 锁对象可以是任意的

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

public voidrun() {while(true){synchronized(newObject()){if(D>0){try{

Thread.currentThread().sleep(100);

}catch(InterruptedException e1) {

e1.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"---"+(D--));

}

}

}

}//run()

View Code

对方法进行锁:private synchronized void function_name(){} 此时锁对象是this

当方法是静态时private static synchronized void function_name(){},锁对象是线程类字节码文件对象。

下面考虑更复杂的情况------死锁

死锁顾名思义就是加的锁打不开,一般发生在当两个线程互相拿着对方的锁即锁嵌套,造成两个线程一直处于等待中。死锁示例:

fb33a976da6142a421081d9587b4b5bb.png

经典的生产者和消费者问题

问题描述:生产者producer生产资源,消费者customer消耗资源呢,我们设计的程序最低保证消费者在消耗资源时必须保证有资源。

设计思想:消费者和生产者共用一个锁,消费者一直消费资源,直到剩余资源数小于规定(0或者业务目标),消费者线程进入等待,直到生产者生产资源后将自己唤醒。

实现:

Producer类

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

packageproducer;importjava.util.ArrayList;public class Producer implementsRunnable {private ArrayListal;public Producer(ArrayListal){this.al =al;

}

@Overridepublic voidrun() {while(true){synchronized(al) {while(al.size()>0){try{

al.wait();//只要有资源,生产线程就wait()

} catch(InterruptedException e) {

e.printStackTrace();

}

}for(int i=0;i<10;i++){

al.add(""+i);

}

al.notify();//生产完成,唤醒等待线程即消费者线程

}

}

}

}

View Code

Customer类

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

packageproducer;importjava.util.ArrayList;public class Customer implementsRunnable {private ArrayListal;public Customer(ArrayListal){this.al =al;

}

@Overridepublic voidrun() {while(true){synchronized(al) {while(al.size()<1){try{

al.wait();//如果没资源,消费者线程就wait()

} catch(InterruptedException e) {

e.printStackTrace();

}

}while(al.size()>0){

System.out.println("消费者:"+al.size());

al.remove(0);

}

al.notify();//消费没了,唤醒等待线程即生产者线程

}

}

}

}

View Code

调用类Demo

f27b204cd3fe403bc2d2cc994c71c5ae.gif

db04d6cce622d35aa63b54a26fd553d7.gif

packageproducer;importjava.util.ArrayList;public classDemo {private static ArrayList al= new ArrayList();public static voidmain(String[] args) {

Producer p= newProducer(al);

Customer c= newCustomer(al);

Thread producer= newThread(p);

Thread customer= newThread(c);

producer.start();

customer.start();

}

}

View Code

6,线程的优化有哪些方法?

线程池

实际业务场景中线程的寿命都很短暂,例如对于网站访问,每个用户请求是一个线程,如果来一个用户,进行一套线程的创建、就绪等动作会严重影响

服务器的响应效率,鉴于此,Java中有了线程池的解决办法,它的思想是程序初始运行时在一个容器内新建固定数量的线程,当用到时从容器内取出一个线程,

线程执行完之后再放回到容器内,实质是以空间换时间,这个容器在Java中就被称为线程池。

Java实现线程池

Executors类工厂类

方法:

public static ExecutorService new FixedThreadPool(int nThreads);//该方法返回一个含有n个线程的线程池接口

线程池接口:ExecutorService

方法:

submit(Runnable task);//将一个线程类加入到线程池

结束:shutdown()

ps:福利:

97a4ee6ceb57db98567b55de424d6129.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值