java线程
学完东西不进脑,那就记下来吧
更多的知识以后补充
文章目录
一. 线程,进程,多线程
先说程序,程序 为指令与数据的有序集合
程序 执行一次就称为一个 进程
而一个 进程 中可以有若干个 线程
线程 是一个独立的执行路径
main()称为线程入口,用于执行整个程序
将一个进程交由多个线程执行,称为多线程
二. 线程创建三种方法
1.Thread class ( 继承Thread类 )
package Thread;
public class TestThread1 extends Thread{
@Override
public void run(){
//run方法线程体
for(int i=0;i<20;i++)
System.out.println("魂--"+i);
}
//main线程---主线程体
public static void main(String[] args){
//先实例化一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
//testThread1.start();
testThread1.start();
for(int i=0;i<20;i++)
System.out.println("左殇--"+i);
}
}
2.Runnable接口(实现Runnable接口)
package Thread;
//创建线程方法2
public class TestThread3 implements Runnable{
@Override
public void run(){
//run方法线程体
for(int i=0;i<20;i++)
System.out.println("魂--"+i);
}
//main线程---主线程体
public static void main(String[] args){
//先实例化一个线程对象
TestThread3 testThread3=new TestThread3();
//创建线程对象,再通过线程对象开启线程(代理)
//Thread thread = new Thread(testThread3);
//thread.start();
new Thread(testThread3).start();
for(int i=0;i<20;i++)
System.out.println("左殇--"+i);
}
}
3.Callable接口(实现Callable接口)
三. 并发问题
多个线程对一个数据进行读写操作时引发问题
public class TestThread4 implements Runnable{
private int ticketNums = 10;
@Override
public void run(){
while(true){
if (ticketNums==0)
break;
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();
}
}
四. 静态代理模式(线程代码逻辑层面的基本原理)
我们所创建的线程称为 真实对象,而 Thread 则称为一个 代理对象
我们重写的 Runnable 就是一个 接口
public class StaticProxy {
public static void main(String[] args) {
Thread_ thread_=new Thread_(new Thread_one());
thread_.run_();
}
}
interface Runnable_{
public void run_();
}
class Thread_one implements Runnable_{
@Override
public void run_(){
System.out.println("Do somethings");
}
}
class Thread_ implements Runnable_{
private Runnable_ target;
public Thread_(Runnable_ target) {
this.target = target;
}
@Override
public void run_(){
before();
this.target.run_();
after();
}
private void before() {
System.out.println("Prepare");
}
private void after() {
System.out.println("Finish");
}
}
五. Lamda表达式
一个接口类里只有一个方法,称为函数式接口
这种函数式接口可以用lambda表达式替代
我们常常会碰到这样一种情况
我们创建了一个类,却只使用了一次这个类
为了是代码不显得冗杂,我们用lambda表达式来解决
package Lambda;
//Lambda表达式
public class TestLambda {
//3.静态内部类
static class Like2 implements TLike{
@Override
public void lambda(){
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
TLike like = new Like();
like.lambda();
TLike like2=new Like2();
like2.lambda();
//4.局部内部类:必须借助接口才能实现,且类没有名字
class Like3 implements TLike{
@Override
public void lambda(){
System.out.println("i like lambda3");
}
}
TLike like3=new Like3();
like3.lambda();
//5.匿名内部类
like = new TLike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6。用Lambda表达式简化
like = ()->{
System.out.println("i like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface TLike{
void lambda();
}
//2.实现类
class Like implements TLike{
@Override
public void lambda(){
System.out.println("i like lambda");
}
}
- 可以用Lambda表达式的条件是 函数式接口
化简的形式如下
//有参数
love = (a)->{代码块};
//无参数
love = ( )->{代码块};
六.线程状态
- 线程的五大状态如图所示
- 常用线程方法
1.线程停止
- JDK不推荐使用线程提供的 stop( ) , destory( ) 方法
- 推荐使用循环次数 + 标志位方法
public class TestStop implements Runnable{
//1.设置一个标志位
private boolean flag = true;
@Override
public void run(){
int i = 0;
while(flag){
System.out.println("Thread run..."+i++);
}
}
//2.设置一个公开的线程,转换标志位
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<100;i++)
{
System.out.println("main"+i);
if (i==99)
{
testStop.stop();
System.out.println("线程停止");
}
}
}
}
2.线程休眠
-
sleep 指定当前线程阻塞毫秒数
-
下面为一个获取系统时间的程序
public class TestSleep{
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
//线程休眠
Thread.sleep(1000);
System.out.println(num--);
if (num<=0){
break;
}
}
}
public static void main(String[] args) {
//打印当前系统时间
//获取系统当前时间
Date startTime = new Date(System.currentTimeMillis());
while(true){
try{
//线程休眠
Thread.sleep(1000);
startTime = new Date(System.currentTimeMillis());//更新当前时间
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
//tenDown();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
3.线程礼让
- 礼让不一定成功
- 礼让为让当前线程从运行状态转为就绪状态
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"虚空").start();
new Thread(myYield,"银月").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
4.线程强制执行
-
用join方法实现
-
主线程与子线程往往同时执行,当使用join方法后,主线程放弃cpu占有权,待当前的子线程执行完后再执行
public class TestJoin implements Runnable{
@Override
public void run() {
for(int i=0;i<10;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<50;i++)
{
if(i==20)
{
thread.join();
}
System.out.println("贫民"+i);
}
}
}
5.线程状态的观测
- 用 Thread.getState( ) 实现
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);
//观察启动后的状态
thread.start();
state = thread.getState();
System.out.println(state);
while(state!=Thread.State.TERMINATED)
{
Thread.sleep(200);
state = thread.getState();
System.out.println(state);
}
}
}
6.线程优先级
- 给予线程执行的优先度
- 设置了优先级不一定按照优先级来执行,只是稍微改变了各个线程抢占资源的能力,具体如何还得看cpu的调度
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,"t1");
Thread t2 = new Thread(myPriority,"t2");
Thread t3 = new Thread(myPriority,"t3");
Thread t4 = new Thread(myPriority,"t4");
//先设置优先级再启动
t1.setPriority(1);
t2.setPriority(2);
t3.setPriority(3);
t4.setPriority(Thread.MAX_PRIORITY);
//设置优先级只是提高了某一线程抢占资源的机会,不能保证一定先执行
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+"-->"+Thread.currentThread().getPriority());
}
}
7.守护线程
先说一下线程的分类
线程分为 用户线程 和 守护线程
一般的线程称为用户线程(Daemon = false)
而 无需考虑程序是否执行完毕,在所有用户线程结束前一直存在 的线程为守护线程(Deamon = true)
常见的守护线程有:记录操作日志,监控内存,垃圾回收等
public class TestDeamon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();
new Thread(you).start();
}
}
class God implements Runnable{
@Override
public void run(){
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I always protect you!");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i=0;i<365;i++)
{
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("happy life");
}
System.out.println("=====Bye world!=====");
}
}
七. 线程同步机制
重点是锁机制,上锁后方法由线程依次调用,主要实现方法为 synchronized 关键字
synchronized(对象){代码块}
这样声明后, 代码块中的对象就被 “上锁了”
{
private int ticketNums = 10;
boolean flag = true;
@Override
public void run(){
while(flag){
buy();
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"虚空").start();
new Thread(ticket,"银月").start();
new Thread(ticket,"邪后").start();
}
//加 synchrinized 后变为同步方法,锁的是实例化后的对象
public void buy(){
synchronized (this){
if(ticketNums>0)
{
System.out.println(Thread.currentThread().getName()
+"拿到了第"+ticketNums--+"黄贴");
}
else
{
flag = false;
}
}
}
}
1.扩充:JUC包
JUC包(java.util.concurrent)中有很多并发编程中常用的工具类
链接
import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i=0;i<1000;i++)
{
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
2.死锁
我们通过下面的案例来看看死锁的情况
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"美人鱼");
Makeup g2 = new Makeup(1,"灰姑娘");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread{
//需要的资源
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
@Override
public void run(){
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆
private void makeup() throws InterruptedException {
if(choice==0)
{
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}
else
{
synchronized (mirror){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(2000);
synchronized (lipstick){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}
}
}
注意以下这两段程序
synchronized (lipstick){
...
synchronized (mirror){
...
}
synchronized (mirror){
...
synchronized (lipstick){
...
}
}
我们知道当代码块执行完毕时锁才会释放,而两个线程却犯了错误,在 手中的锁还未释放时 就申请要对方的锁,导致两把锁(mirror, lipstick)都无法释放,变成了死锁。
按以下方式修改便可解决
if(choice==0)
{
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(50);
}
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
else
{
synchronized (mirror){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(50);
}
synchronized (lipstick){
System.out.println(this.girlName+"获得镜子的锁");
}
}
总结: 死锁的出现原因可总结为以下条件的共同作用
3.Lock对象
- 重点!!!
- 前面的synchronized称为同步方法,而这里是区别于前者的显示声明锁的方法
- 在线程编写时推荐使用
class TestLock2 implements Runnable{
private int ticketnums = 10;
private final ReentrantLock lock = new ReentrantLock();//锁的声明
@Override
public void run()
{
lock.lock();
try{//注意搭配try,finally使用
System.out.println("第"+ticketnums--+"张票");
}finally {
lock.unlock();
}
}
}
八. 线程通讯协作(生产者消费者问题)
1.管程法
- 用 notifyAll 和 wait 实现
//管程法
public class TestPC {
public static void main(String[] args) {
SynContainer container=new SynContainer();
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+"只鸡");
try {
container.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run(){
for (int i=0;i<100;i++){
try {
System.out.println("消费者-->第"+container.pop().id+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器大小
Chicken[] chickens=new Chicken[10];
//容器计数器
int cnt = 0;
//生产者放入产品
public synchronized void push(Chicken chicken) throws InterruptedException {
//如果容器满了,等待消费者消费
if(cnt==chickens.length)
{
//等待消费者
this.wait();
}
//如果没有满,我们就需要丢入产品
chickens[cnt]=chicken;
cnt++;
//可以通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() throws InterruptedException {
if(cnt==0)
{
//等待生产者
this.wait();
}
//如果可以消费
cnt--;
Chicken chicken = chickens[cnt];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
2.信号灯法
- 设定一个标志位,线程运行时根据 flag 状态来确定是否 wait()
//信号灯法
public class TestPC2 {
public static void main(String[] args) {
Movie movie = new Movie();
new Player(movie).start();
new Watcher(movie).start();
}
}
//生产者-->演员
class Player extends Thread{
Movie movie;
public Player(Movie movie) {
this.movie = movie;
}
public void run()
{
for (int i=0;i<20;i++)
{
if(i%2==0)
{
this.movie.play("Tears");
}
else
{
this.movie.play("Matrix");
}
}
}
}
//消费者-->观众
class Watcher extends Thread{
Movie movie;
public Watcher(Movie movie) {
this.movie = movie;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.movie.watch();
}
}
}
//产品-->节目
class Movie{
//演员表演观众等待 T
//观众观看,演员等待 F
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;
}
}
九. 线程池
经常性地创建与销毁线程,会导致消耗大量的资源。我们可以选择提前创建好若干个线程,放入线程池中,使用的时候直接获取,使用完放回池中,可以实现线程的重复利用。
public class TestPool {
public static void main(String[] args) {
//1.创建线程池
//ExecutorService service = Executors.newFixedThreadPool(int nThread); 参数为线程池的大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}