一、线程简介
process和thread
- 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
- 进程则是程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
- 通常一个进程里面包含多个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
- 注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU(多核),如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,以为切换的很快,所以就有了同时执行的错觉。
核心概念
- 线程就是独立的执行路径;
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
- main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关,先后顺序是不能人为干预的。
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如CPU调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
二、线程创建
线程创建的三种方法:
第一种方法,继承Thread类:
代码:
package JDB.com.day3;
// 总结:线程开启不一定立刻执行,由CPU调度
public class TestThread1 extends Thread {
@Override
public void run(){
// run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
// main主线程
// 创建一个线程对象
TestThread1 testThread1 = new TestThread1();
// 调用start()方法开启线程 ,start方法会让多线程同时执行。
testThread1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
网图下载:
package JDB.com.day3;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
// 实现多线程下载图片
public class TestThread2 extends Thread {
private String url;
private String name;
public TestThread2(String url,String name){
this.url=url;
this.name=name;
}
// 下载图片线程的执行体
@Override
public void run(){
WebDownloader webDownloader= new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载文件名字是"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","1");
TestThread2 t2 = new TestThread2("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","2");
TestThread2 t3 = new TestThread2("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","3");
t1.start();
t2.start();
t3.start();
}
// 下载器
class WebDownloader{
// 下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
}
第二种方法,实现Runnable接口:
代码:
package JDB.com.day3;
public class TestThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
// 创建runna接口的实现对象
TestThread3 testThread3 = new TestThread3();
// 创建线程对象,提供线程对象来开启我们的线程,代理
new Thread(testThread3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
两种方法的对比:
并发例子:
package JDB.com.day3;
// 多个线程操作同一个资源下,线程不安全,数据混乱
public class TestThread4 implements Runnable {
private int ticketNums = 50;
@Override
public void run() {
while(true){
if (ticketNums<=0){
break;
}
// 模拟延迟
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"师傅").start();
}
}
代码:
package JDB.com.day3;
public class Race implements Runnable{
private static String winner ;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛什么时候结束
boolean flag = gameOver(i);
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName()+"--->跑了"+i+"步");
}
}
public boolean gameOver(int steps){
if (winner!=null){
return true;
}{
if (steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
第三种方法,实现Callable接口(了解):
代码:
package JDB.com.day3;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
// 实现多线程下载图片
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url,String name){
this.url=url;
this.name=name;
}
// 下载图片线程的执行体
@Override
public Boolean call(){
WebDownloader webDownloader= new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载文件名字是"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","1");
TestCallable t2 = new TestCallable("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","2");
TestCallable t3 = new TestCallable("https://img14.360buyimg.com/n0/jfs/t1/191889/5/11924/221265/60e3c4c1E6db4d0ca/20217c8d67d813f2.jpg","3");
// 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获得结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdown();
}
// 下载器
class WebDownloader{
// 下载方法
public void downloader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
}
三、Lamda表达式
8种写法,详解Lambda表达式怎么来的
注意:
1,lambda有一行代码的情况下,才能简化成一行,如果多行,则用代码块包装
2,前提是接口为函数式接口
3,多个参数也可以去掉参数,要去掉就要都去掉,必须加上括号
package com.chenxb.myLambda;
public class MyLambda {
public static void main(String[] args) {
//第一种写法,通过实现类
Dog dog = new Dog();
dog.setName("inplements");
//第二种写法,通过实现类,简化
new Dog().setName("implements2");
//第三种写法,匿名内部类
IAnimal iAnimal = new IAnimal() {
@Override
//这是匿名内部类当中的方法,重写了抽象类的方法
public void setName(String name) {
System.out.println(name);
}
};
iAnimal.setName("anonymous");
//第四种写法,匿名内部类,再简化
new IAnimal() {
@Override
//这是匿名内部类当中的方法,重写了抽象类的方法
public void setName(String name) {
System.out.println(name);
}
}.setName("anonymous2");
//第五种写法,lambda
IAnimal iAnimalLambda = (String name) -> {
System.out.println(name);
};
iAnimalLambda.setName("lambda");
//第五种写法,lambda,去掉参数类型
IAnimal iAnimalLambda2 = (name) -> {
System.out.println(name);
};
iAnimalLambda2.setName("lambda2");
//第六种写法,lambda,去掉括号
IAnimal iAnimalLambda3 = name -> {
System.out.println(name);
};
iAnimalLambda3.setName("lambda3");
//第六种写法,lambda,去掉中括号
IAnimal iAnimalLambda4 = name -> System.out.println(name);
iAnimalLambda4.setName("lambda4");
//第七种写法,lambda,代码块
IAnimal iAnimalLambda5 = name -> {
System.out.println(name);
System.out.println(name);
};
iAnimalLambda5.setName("lambda5");
//第八种写法,lambda,多个参数可以去掉参数类型,要同时去掉
IPeople iPeople = (name,age) -> System.out.println(name + " " + age);
iPeople.setName("lambda more para",18);
/*注意:
1,lambda有一行代码的情况下,才能简化成一行,如果多行,则用代码块包装
2,前提是接口为函数式接口
3,多个参数也可以去掉参数,要去掉就要都去掉,必须加上括号
*/
}
}
interface IPeople {
public abstract void setName(String name, int age);
}
class Man implements IPeople {
@Override
public void setName(String name, int age) {
System.out.println(name + " " + age);
}
}
interface IAnimal {
public abstract void setName(String name);
}
class Dog implements IAnimal {
@Override
public void setName(String name) {
System.out.println(name);
}
}
四、线程状态
具体分析:
线程方法:
停止线程:
stop方法代码:
package JDB.com.day4;
public class TestStop implements Runnable {
// 设置一个标志位
private Boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run...Thread"+i++);
}
}
//设置一个公开方法停止线程
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop =new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if (i==900){
// 调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程休眠:
利用线程休眠模拟倒计时:
package JDB.com.day4;
public class TestSleep {
public static void main(String[] args) {
tenDown();
}
public static void tenDown(){
int num = 10;
while (true){
try {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0){
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
获得系统的当前时间:
package JDB.com.day4;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) {
// tenDown();
Date startTime = new Date(System.currentTimeMillis());// 获得当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());// 更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void tenDown(){
int num = 10;
while (true){
try {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0){
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程礼让:
礼让代码:
package JDB.com.day4;
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();// 礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程 Join:
Join代码:
package JDB.com.day4;
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if (i == 200){
thread.join();
}
System.out.println("main"+i);
}
}
}
线程状态观测:
代码:
package JDB.com.day4;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("");
});
// 观察状态
Thread.State state= thread.getState();
System.out.println(state);//new
// 1观察启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);// Run
while (state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
输出结果:
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TERMINATED
注意:死亡以后的线程不能再次启动。
线程优先级
代码:
package JDB.com.day4;
public class TestPriority {
public static void main(String[] args) {
// 主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
// 先设置优先级,再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
t5.setPriority(8);
t5.start();
t6.setPriority(7);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
守护daemon线程
代码:
package JDB.com.day4;
public class TestDaemon {
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(); //god守护线程启动
new Thread(you).start(); // 用户线程启动
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("god保佑你");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一辈子都开心的活着");
}
System.out.println("=======goodbye!world!===");
}
}
五、线程同步
并发:同一个对象被多个线程同时操作
public class Bank {
private int count =0;//账户余额
//存钱
public synchronized void addMoney(int money){
count +=money;
System.out.println(System.currentTimeMillis()+"存进:"+money);
}
//取钱
public synchronized void subMoney(int money){
if(count-money < 0){
System.out.println("余额不足");
return;
}
count -=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查询
public void lookMoney(){
System.out.println("账户余额:"+count);
}
}
同步代码块 :
即有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
代码如:
synchronized(object){
}
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
public class Bank {
private int count =0;//账户余额
//存钱
public void addMoney(int money){
synchronized (this) {
count +=money;
}
System.out.println(System.currentTimeMillis()+"存进:"+money);
}
//取钱
public void subMoney(int money){
synchronized (this) {
if(count-money < 0){
System.out.println("余额不足");
return;
}
count -=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查询
public void lookMoney(){
System.out.println("账户余额:"+count);
}
}
这样也实现了线程同步,运行效率上来说也比方法同步效率高,同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
代码:
package com.trs.thread;
/**
* 死锁示例
* @author xiayunan
* @date 2018年7月14日
*
*/
public class TestDeadLock2 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread5(0));
Thread t2 = new Thread(new MyThread5(1));
t1.start();
t2.start();
}
}
class MyThread5 implements Runnable{
private static Object o1 = new Object();
private static Object o2 = new Object();
private int flag;
public MyThread5(int flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag==0){
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"锁住了o1");
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}else{
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"锁住了o2");
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}
}
}
代码:
六、线程协作
解决方法管程法:
public class tuble {
public static void main(String[]args)
{
SynContainer container=new SynContainer();
new Productor(container).start();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者,将数据存入容器
class Productor extends Thread
{
SynContainer container;
public Productor(SynContainer container)
{
this.container=container;
}
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println("生产第"+i+"个馒头");
container.push(new Data(i));
}
}
}
//消费者,将数据从容器取出
class Consumer extends Thread
{
SynContainer container;
public Consumer(SynContainer container)
{
this.container=container;
}
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println("消费第"+container.pop().id+"个馒头");
}
}
}
//缓冲区
class SynContainer
{
Data[] datas=new Data[10]; //缓冲区里面存取数据
int count=0;//计数器
//存
public synchronized void push(Data data)
{
//何时能生产
if(count==datas.length)
{
try {
this.wait();//线程阻塞,消费者通知生产才解除阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
datas[count]=data;
count++;
this.notify(); //存在数据,通知消费者可消费
}
//取
public synchronized Data pop()
{
//何时能取:
if(count==0)
{
try {
this.wait();//线程阻塞,生产者通知消费解除阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count--;
Data data=datas[count];
this.notifyAll();//存在空间,通知生产者可生产
return data;
}
}
//数据
class Data
{
int id;
public Data(int id)
{
this.id=id;
}
}
信号灯法:
package duoxiancheng;
//测试生产者消费者问题,信号灯法
public class TestPc2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者:演员
class Player extends Thread {
TV tv = new TV();
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2 == 0) {
this.tv.play("快乐大本营播放中");
} else{
this.tv.play("抖音");
}
}
}
}
//消费者:观众
class Watcher extends Thread {
TV tv = new TV();
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品:节目
class TV {
//演员表演时候,观众等待
//观众观看,演员等待
String voice; //表演节目
boolean flag = true;
//表演
public synchronized void play(String voice) {
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了" + voice);
//通知观众观看
this.notifyAll();
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch() {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了" + voice);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
代码:
结果:
多线程知识点思维导图