核心概念
-
线程就是独立的执行路径
-
Main()称之为主线程,为系统的入口,用于执行整个程序
-
在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
-
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
-
线程会带来额外的开销,如cpu调度时间,并发控制开销
-
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
Demo1 简单理解线程
public class Demo1 extends Thread {
@Override
public void run() {
//super.run();
for (int i = 0; i < 20; i++) {
System.out.println(" 子线程在跑 "+ i);
}
}
public static void main(String[] args) {
Demo1 demo1 =new Demo1();
//demo1.run();//普通的执行run,正常顺序
demo1.start();//创建子线程,执行run,主线程和子线程的执行,由CPU进行调度,无法人为干预
//
for (int i = 0; i < 100; i++) {
System.out.println("主线程在跑"+i);
}
}
}
/*
主线程在跑0
子线程在跑 0
子线程在跑 1
子线程在跑 2
子线程在跑 3
子线程在跑 4
主线程在跑1
主线程在跑2
主线程在跑3
主线程在跑4
主线程在跑5
主线程在跑6
主线程在跑7
主线程在跑8
主线程在跑9
主线程在跑10
主线程在跑11
主线程在跑12
主线程在跑13
主线程在跑14
主线程在跑15
主线程在跑16
主线程在跑17
子线程在跑 5
子线程在跑 6
主线程在跑18
主线程在跑19
子线程在跑 7
子线程在跑 8
子线程在跑 9
子线程在跑 10
子线程在跑 11
子线程在跑 12
子线程在跑 13
子线程在跑 14
子线程在跑 15
子线程在跑 16
子线程在跑 17
子线程在跑 18
子线程在跑 19
主线程在跑20
主线程在跑21
*/
Demo2 下载网图
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class Demo2 {
int k=0;
public static void main(String[] args) {
WebDownloader wd =new WebDownloader("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
WebDownloader wd2 =new WebDownloader("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
wd.start();
wd2.start();
/*
下载了:1.png
下载了:2.jpg
*/
}
}
class WebDownloader extends Thread{
private String Url;
private String ServerFileName;
public WebDownloader(String url,String serverFileName)
{
this.Url = url;
this.ServerFileName=serverFileName;
}
@Override
public void run() {
//super.run();
try {
//copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
System.out.println("下载了:"+this.ServerFileName);
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载图片报错");
}
}
}
Demo3 使用Runable接口实现多线程
public class Demo3 implements Runnable {
@Override
public void run() {
//super.run();
for (int i = 0; i < 20; i++) {
System.out.println(" 子线程在跑 "+ i);
}
}
public static void main(String[] args) {
Demo3 demo3 =new Demo3();
new Thread(demo3).start();//调用方式稍作改变
//
for (int i = 0; i < 100; i++) {
System.out.println("主线程在跑"+i);
}
}
}
Demo4 使用Callable的接口实现多线程
public class Demo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
WebDownloader2 wd =new WebDownloader2("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
WebDownloader2 wd2 =new WebDownloader2("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
ExecutorService service = Executors.newFixedThreadPool(2);
Future<Boolean> res1 = service.submit(wd);
Future<Boolean> res2 = service.submit(wd2);
Boolean r1 = res1.get();
Boolean r2 = res2.get();
System.out.println(r1);
System.out.println(r2);
}
}
class WebDownloader2 implements Callable<Boolean> {
private String Url;
private String ServerFileName;
public WebDownloader2(String url,String serverFileName)
{
this.Url = url;
this.ServerFileName=serverFileName;
}
@Override
public Boolean call() {
//super.run();
try {
//copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
System.out.println("下载了:"+this.ServerFileName);
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载图片报错");
}
return true;
}
}
静态代理模式
Thread 实现对象执行方法的模式
//总结:
//真是执行对象和代理对象都要集成同一个接口
//代理对象要代理真实角色
//好处
//1.代理对象可以做很多真实对象做不了的事,或者说不好做的事
//2.真实对象专注自己的业务
public class Demo6 {
public static void main(String[] args) {
Person person = new Person();
//静态代理类传入 被代理对象
MarryCompany marryCompany = new MarryCompany(person);
marryCompany.HappyMarry();//静态代理类,帮助被代理对象执行
//Tread 类实现新线程执行对象方法就是静态代理模式
new Thread(()-> System.out.println("Tread 类实现新线程执行对象方法就是静态代理模式")).start();
}
}
/**
* Marry接口
*/
interface Marry
{
void HappyMarry();
}
/**
* Person类,静态代理实际执行的对象类
*/
class Person implements Marry
{
@Override
public void HappyMarry() {
System.out.println("结婚啦");
}
}
/**
* MarryCompany Marry代理类
*/
class MarryCompany implements Marry
{
private Marry _marryPerson;
public MarryCompany(Marry marryPerson)
{
this._marryPerson =marryPerson;
}
@Override
public void HappyMarry() {
System.out.println("收定金,布置会场");
this._marryPerson.HappyMarry();
System.out.println("收尾款");
}
}
线程状态
五种状态
创建状态 就绪状态 阻塞状态 运行状态 死亡状态
Sleep
Sleep阻塞线程,模拟网络延时可以放大问题的放生性。
public class Demo7 {
public static void main(String[] args) throws InterruptedException {
Date currentTime = new Date(System.currentTimeMillis());
for (int i = 0; i < 10; i++) {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(currentTime));//java操作时间和c#写起来感觉还是有很大区别的
Thread.sleep(1000);
currentTime = new Date(System.currentTimeMillis());
}
}
}
线程礼让 yeild
让CPU重新调度,礼让不一定成功,看CPU调度
public class Demo8 {
public static void main(String[] args) {
TestYeild testYeild1 = new TestYeild();
TestYeild testYeild2 = new TestYeild();
new Thread(testYeild1,"a").start();
new Thread(testYeild2,"b").start();
}
}
class TestYeild implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始运行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束运行");
}
}
Join
Join合并线程,待此线程执行完成之后,在执行其他线程
public class Demo9 {
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
for (int i = 0; i < 100; i++) {
if(i==0)
{
thread.start();
}
if(i==10)
{
System.out.println("开始插队=================");
thread.join();
}
System.out.println("主线程"+i);
}
}
}
class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("插队线程"+i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("插队还不快点,还睡觉==============");
}
}
/*
主线程0
主线程1
主线程2
主线程3
主线程4
主线程5
主线程6
主线程7
主线程8
插队线程0
主线程9
开始插队=================
插队线程1
插队线程2
插队线程3
插队线程4
插队线程5
插队线程6
....
插队线程43
插队线程44
插队线程45
插队线程46
插队线程47
插队线程48
插队线程49
插队还不快点,还睡觉==============
主线程10
主线程11
主线程12
....
主线程88
主线程89
主线程90
主线程91
主线程92
主线程93
主线程94
主线程95
主线程96
主线程97
主线程98
主线程99
Process finished with exit code 0
*/
线程优先级
线程优先级高不一定先执行,但权重高!
min=1 max=10 normal=5
先设置优先级再启动
守护线程
线程分为用户线程和守护线程。虚拟机必须确保用户线程执行完毕。虚拟机不用等待守护线程执行完毕。如后台记录操作日志,监控内存,垃圾回收等待。
public class Demo10 {
public static void main(String[] args) {
DeamonThread deamon = new DeamonThread();
UserThread user = new UserThread();
Thread deamonThread = new Thread(deamon);
deamonThread.setDaemon(true);//守护线程默认为false
deamonThread.start();
Thread userThread = new Thread(user);
userThread.start();
}
}
class UserThread implements Runnable
{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("用户线程执行第"+i);
}
}
}
class DeamonThread implements Runnable
{
@Override
public void run() {
//当主线程和其他用户线程执行完毕之后,守护线程也随之关闭
while(true){
System.out.println("守护线程执行中");
}
}
}
同步方法和同步快
public class Demo4 implements Runnable {
int ticket = 10;
@Override
public synchronized void run() {//锁住run方法
while (ticket>0)
{
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
}
}
public static void main(String[] args) {
Demo4 demo4 = new Demo4();
new Thread(demo4,"小明").start();
new Thread(demo4,"小红").start();
new Thread(demo4,"小蓝").start();
}
}
import java.util.ArrayList;
import java.util.List;
public class Demo11 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();//这里实例化跟C#不太一样
for (int i = 0; i < 20000; i++) {
new Thread(()->{
synchronized (list)
{
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(6000);
System.out.println(list.size());//不加 synchronized的情况下 19997!=20000,add当前对象的相同位置,覆盖了
}
}
public class Demo12 {
public static void main(String[] args) {
Account account = new Account("共同账户",100);
Withdraw withdraw = new Withdraw(account,100);
new Thread(withdraw).start();
new Thread(withdraw).start();
new Thread(withdraw).start();
new Thread(withdraw).start();
}
}
class Account{
String name;
int money;
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
class Withdraw extends Thread
{
Account account;
int withdraw;
int nowMoney;
public Withdraw(Account account, int withdraw) {
this.account = account;
this.withdraw = withdraw;
}
@Override
public void run()
{
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (account)
{
if(account.money>0 && account.money>=withdraw)
{
int rs = this.account.money-this.withdraw;
this.account.money -=this.withdraw;
this.nowMoney += this.withdraw;
System.out.println("余额"+rs+";现在手中的钱有:"+nowMoney);
}
else
{
System.out.println("余额不足");
}
}
}
}
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo13 {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();//这里实例化跟C#不太一样
for (int i = 0; i < 20000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(6000);
System.out.println(list.size());//CopyOnWriteArrayList 线程安全类
}
}
生产者消费者问题
解决方案一:
并发写作模型“生产者/消费者模式”-->管程法
生产者:负责生产数据的模块
消费者:负责处理数据的模块
缓冲区:消费者不能直接使用生产者的数据,消费者从缓冲区拿出数据
解决方案二:
并发协作模型“生产者/消费者模式”-->信号灯法
通过标志位,判断是否可以通行
public class Demo14 {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Cook cook =new Cook(synContainer);
Custom custom = new Custom(synContainer);
new Thread(cook,"cook").start();
new Thread(custom,"Custom").start();
}
}
//产品 鸡
class Chicken
{
public int id;
public Chicken(int id) {
this.id = id;
}
}
//生产者
class Cook implements Runnable
{
SynContainer synContainer;
public Cook(SynContainer synContainer)
{
this.synContainer = synContainer;
}
@Override
public void run() {
// 生产者生产 产品
for (int i = 0; i < 100; i++) {
Chicken chicken = new Chicken(i);
this.synContainer.push(chicken);
System.out.println("生产者生产了第"+i+"只鸡");
}
}
}
//消费者 顾客
class Custom implements Runnable
{
SynContainer synContainer;
public Custom(SynContainer synContainer)
{
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Chicken chicken = this.synContainer.pop();
System.out.println("消费者消费了第"+chicken.id+"只鸡");
}
}
}
class SynContainer
{
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void push(Chicken chicken)
{
//容器满了,等待生产者消费
if(chickens.length == count)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.chickens[count]=chicken;
count++;
//通知消费者,可以消费了
this.notifyAll();
}
public synchronized Chicken pop() {
if (count == 0) {
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Chicken chicken = this.chickens[count-1];
//this.chickens[count] = null;
count--;
this.notifyAll();
return chicken;
}
}
notify和notifyAll的差别:notify只唤醒一个线程,且不保证能那个线程会被唤醒,这取决于线程调度器。notifyAll会让等待该锁的所有线程都会被唤醒。
线程池
思路:提前创建好多个线程池,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
提高相应速度(减少了创建新线程的时间)
降低消耗资源(重复利用线程池中线程,不需要每次创建)
便于管理(核心池的大小(放多少线程)、最大线程数(同时跑多少线程)、线程没有任务时最多保持多长时间)