个人理解:

  在相同的进程也就是运行同样的程序的前提下,线程越多效率越快!当然硬件也是个障碍!为了提高效率,可以多创建线程,但是也不是越多越好,这就需要了线程池进行管理!需要知道的线程实现的方法:继承Thread类和实现Runnable方法!了解其状态、启动是Start!多线程存在时有可能出现不安全或者异常的情况:这样就需要保证其安全的线程同步的方法!

死锁:https://blog.csdn.net/qdh186/article/details/86497809?tdsourcetag=s_pcqq_aiomsg(侵删)

一、多线程介绍:

1、进程:

    正在执行的程序;

2、线程:

    进程中的一个执行单元,负责当前进程中程序的执行。

  一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

3、单线程与多线程的比较:

  单线程:多个任务依次执行,排着队来;

  多线程:多个任务同时执行;

二、程序运行原理:

1、分时调度:

  所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间;

2、抢占式调度:

  优先让优先级高的线程使用cpu,优先级相同,则随机选择!(java就是这样的)

cpu使用抢占式调度模式在多个线程间进行高速的切换。某个时刻,只执行 一个线程。多个线程并不能提高程序的运行速度,但能提高程序运行效率,让CPU的使用率更高。

三、主线程:

  jvm启动后,必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行。

四、Thread类:

    程序中的执行线程!!!

public class MyThread extends Thread {

    public void run() {
//获取当前线程的名称getName()
System.out.println("线程名称为"+getName());
for(int i=0;i<20;i++){
System.out.println("myThread-"+i);
}
}
}
public class Demo01 {
public static void main(String[] args) {
//获取指定当前代码的线程的线程对象(在main方法里必须先获得其对象,因为main方法是静态的,在其内部不能访问普通方法!)
Thread th=Thread.currentThread();
//获取该线程的名字
System.out.println(th.getName());
// 创建新线程---(需要描述任务和开启线程)
MyThread thread = new MyThread();
// 开启线程
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("mian-" + i);
} }
}

1、线程对象调用 run方法和调用start方法区别?

线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

2、创建线程的目的是什么?(多线程就是多个栈)

是为了建立程序单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。

3、获取线程名称:

Thread.currentThread().getName();获取当前线程对象的名称

五、实现Runnable接口创建线程方式:

public class MyRunnable implements Runnable{

    public void run() {
for(int i=0;i<20;i++){
System.out.println("thread-"+i);
} }
}
public class Demo01 {
public static void main(String[] args) {
//创建线程任务对象(负责描述任务)
MyRunnable mr=new MyRunnable();
//创建thread对象(只需要开启线程)
Thread th=new Thread(mr);
//开启线程
th.start();
for(int i=0;i<20;i++){
System.out.println("main-"+i);
}
}
}

1、原理:

  实现Runnable接口,避免了继承Thread类的单继承局限性(不能继承其他的类了)。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

  建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

2、好处:

  第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。

3、匿名内部类使用:

public class Demo02 {
public static void main(String[] args) {
/* 匿名内部类:
new 子类或实现类名(){
需要重写的方法
}*/
//1.创建线程的方式:继承Thread (重写run方法:打出run 后 alt+/)
new Thread(){
public void run() {
System.out.println(getName()+"这是新线程任务");
}
}.start();
//2.实现Runnable接口
Runnable run=new Runnable(){
public void run() {
System.out.println(Thread.currentThread().getName()+"这是第二种方式");
};
};
new Thread(run).start();
}
}

六、线程状态:

七、线程池:

1、概念:

  线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复的使用,

省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。(正常的话,其实就是在线程池的集合中remove线程,用完后再add回去)

2、Runnable接口:

  线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

public class MyRun implements Runnable{

    public void run() {
//获取执行当前线程对象的名字
String name=Thread.currentThread().getName();
for(int i=0;i<20;i++){
System.out.println(name+i);
//不能throws
try {
Thread.sleep(1000);//休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
public class Demo01 {
public static void main(String[] args) {
//创建线程任务对象
MyRun mr= new MyRun();
//创建新线程对象
Thread th =new Thread(mr);
//开启线程
th.start();
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Demo02 {
public static void main(String[] args) {
//用线程池的方式完成线程任务
//1.从线程池工厂中获取一个装有两条线程的线程池对象
ExecutorService es=Executors.newFixedThreadPool(2);
//2.创建线程任务
MyRun mr=new MyRun();
//3.让线程池自主选一条线程执行线程任务(第二个会等第一个的有空了就执行)
es.submit(mr);
es.submit(mr);
es.submit(mr);
/*//关闭线程池(一般不关闭)
es.shutdown();*/
}
}

3、Callable接口:有泛型,与返回值类型相同(与runnable多个返回值)

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String>{

    public String call() throws Exception {

        return "abc";
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class Demo01 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1.从线程池工厂中获取线程池对象
ExecutorService es=Executors.newFixedThreadPool(2);
//2.创建线程任务对象
MyCallable mc=new MyCallable();
//3.让线程池自主选择一条线程执行线程任务
Future<String> f=es.submit(mc);
//4.获取线程任务的返回值
System.out.println(f.get());
}
}

Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

get() 获取Future对象中封装的数据结果

import java.util.concurrent.Callable;

public class MyCall implements Callable<Integer>{
private int num;
private int num1;
public MyCall(){}
public MyCall(int num,int num1){
this.num=num;
this.num1=num1;
}
public Integer call() throws Exception {
//计算求和
int sum=0;
for(int i=num;i<=num1;i++){
sum+=i;
}
return sum;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class Demo01 {
//用线程池计算50.。。。100的和------44.....200的和
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1.从线程池工厂获取线程池对象
ExecutorService es=Executors.newFixedThreadPool(2);
//2.创建线程任务对象
MyCall mc1=new MyCall(50,100);
MyCall mc2=new MyCall(44,200);
//3.让线程池自主选择线程执行任务
Future<Integer> f1=es.submit(mc1);
Future<Integer> f2=es.submit(mc2);
//4.获取线程任务返回值
int sum1=f1.get();
int sum2=f2.get();
System.out.println(sum1);
System.out.println(sum2);
}
}

八、多线程的线程安全:

1、线程安全:

程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

2、线程同步:Synchronized

①、同步代码块:锁对象是任意的对象,但是多个线程时,必须是同一个锁!

  synchronized (锁对象) {

    可能会产生线程安全问题的代码

  }

public class Tickets implements Runnable {
// 创建售票类
// 定义100张票
private int tickect = 100;
// 定义同步锁--必须定义在外面 表示所有的线程都使用同一个锁!
Object obj = new Object(); public void run() {
while (true) {
/*同步代码块将可能会产生线程安全问题的代码包起来---
各个线程经过时都需要带着锁obj而进行下一步,如果没有的话需要在此等待。
同时这个线程结束的时候将锁归还,这样下一个线程就可以拿着这个锁进行下一步操作!
obj可以写成本类对象的this,因为其唯一性,不需要上面的定义!*/
synchronized (obj) {
if (tickect > 0) {
try {
//假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 先赋值后--
System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
}
} }
}
}
public class Demo01 {
public static void main(String[] args) {
//1.创建线程任务
Tickets tickets=new Tickets();
//创建三个线程完成同一个任务
Thread t1=new Thread(tickets);
Thread t2=new Thread(tickets);
Thread t3=new Thread(tickets);
//开启线程
t1.start();
t2.start();
t3.start();
}
}

②、同步方法:锁对象是this

  public synchronized void method(){

    可能会产生线程安全问题的代码

  }

  

public class Tickets2 implements Runnable {
// 创建售票类
// 定义100张票
private int tickect = 100;
// 定义同步锁--必须定义在外面 表示所有的线程都使用同一个锁!
Object obj = new Object(); public void run() {
while (true) {
method();
}
}
public synchronized void method() {
if (tickect > 0) {
try {
// 假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 先赋值后--
System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
}
}
}

③、静态同步方法: 在方法声明上加上static synchronized

  public static synchronized void method(){

    可能会产生线程安全问题的代码

  }

静态同步方法中的锁对象是 类名.class(字节码(.class)文件的对象<字节码文件一进内存就自动生成字节码文件对象>唯一性)

3、lock接口:

lock 获取锁

unlock 释放锁

package com.oracle.demo01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Tickets3 implements Runnable { // 定义100张票
private int tickect = 100;
//创建锁接口对象---用于解决可能出现因为异常而导致锁不被还回来
Lock lock=new ReentrantLock();
public void run() {
while (true) {
//获取锁
lock.lock();
if (tickect > 0) {
try {
// 假设休眠50--这里是制造线程不安全的情况!只是举例的一种!!
Thread.sleep(50);
System.out.println(Thread.currentThread().getName() + ":出售第" + tickect-- + "张票");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//释放锁
lock.unlock();
}
// 先赋值后-- } }
}
}

最新文章

  1. 应用程序框架实战二十九:Util Demo介绍
  2. 基于Spring设计并实现RESTful Web Services(转)
  3. js变量申明提前及缺省参数
  4. SQL Cursor 游标的使用
  5. 将多个Sheet导入到同一个Excel文件中
  6. 升级10.10 Yosemite 后,cocoapods 出现错误(解决方案)
  7. 如何在word中写出赏心悦目的代码
  8. css基础和心得(二)
  9. 搭建ruby环境
  10. Android Service 基础
  11. 移动设备分辨率(终于弄懂了为什么移动端设计稿总是640px和750px)
  12. masonry 基本用法
  13. SysUtils.CompareText的注释
  14. webpack 解决跨域问题
  15. mysql 分配内存大小配置
  16. 饭后来份TempData,瞅瞅有啥料
  17. 【BZOJ1051】[HAOI2006]受欢迎的牛
  18. JSON的多样格式
  19. 浏览器兼容性问题解决方案之CSS,已在IE、FF、Chrome测试
  20. SpringMVC学习记录二——非注解和注解的处理器映射器和适配器

热门文章

  1. 02_SQliteOpenHelper介绍&amp;oncreate方法介绍
  2. VC代码生成里面的/MT /MTd /MD /MDd的意思
  3. 《精通Spring4.X企业应用开发实战》读后感第四章(BeanFactory生命周期)
  4. 细说 CSS margin
  5. 截图上传功能 imageAreaselect
  6. 732. My Calendar III (prev)
  7. 卸载/安装ubuntu系统教程
  8. python寻找小于给定值的最大质数
  9. vue路由守卫
  10. UGUI CanvasGroup