【线程笔记】

创建


继承创建

package com.dto;

/**
 * 多线程的创建
 * 1、继承Thread类
 * 2、重写里面的run方法 方法体的执行 线程需要做什么。
 * 3、创建Thread的子类对象
 * 4、使用对象调用start方法
 */
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.start();
        MyThread02 myThread02=new MyThread02();
        myThread02.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2==0){
                System.out.println(i);
            }
        }
    }
}

class MyThread02 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2!=0){
                System.out.println(i);
            }
        }
    }
}

创建问题 

只使用run方法  单纯的方法调用  没有使用线程

调用多次start会出现异常(源码中有声名)

  

查看线程的名字


使用匿名内部类 重写方法。


Thread类的常用方法

/**
 * start  :启动线程  调用run方法
 * run    :执行线程的内容的操作
 * currentThread :返回当前线程,Thread.crrentThread()  [当线程执行到这里时就会放回当前的]
 * getName 对象名.
 * setName 对象名.   (也可使用构造器得到 )
 * yield   :释放当前cpu给线程的时间
 * join    :线程1.join  放在线程2的执行里面  需要线程一执行完才可以  插队 插队执行完才可以                 执行线程2 线程2阻塞.
 * stop    :已过时  强制线程停止。
 * sleep   :睡眠多少毫秒  当前线程阻塞状态
 * isAlive :是否存活
 * getPriority :得到优先级
 * setPriority :设置优先级  1——10
 *  设置优先级需要在start开始之前  也只是概率高 也不是一定有
 * 
 */

@Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2!=0){
                System.out.println(this.getName()+" "+i);
            }
            if (i%3==0){
                yield();
                //this.yield();
                //Thread.currentThread().yield();

            }
        }
    }
public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.start();
        MyThread02 myThread02=new MyThread02();
        myThread02.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2==0){
                System.out.println(i);
            }
        }
    }
}

class MyThread02 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2!=0){
                System.out.println(this.getName()+" "+i);
            }
            if (i%3==0){
                MyThread myThread=new MyThread();
                try {
                    myThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class MyThread02 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i%2!=0){
                System.out.println(this.getName()+" "+i);
            }
            if (i%3==0){
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


经典练习:窗口卖票


//每个线程都有100张票 其实总共300张
//  使用private static int ticket=100;  静态变量有一份  可以暂时解决。。
public class Window extends Thread{
    private int ticket=100;

    @Override
    public void run() {
        while (true){
            if (ticket>0){
                System.out.println(getName()+"卖票了"+"票号为:"+ticket);
                ticket--;
            }else {
                System.out.println("卖完了");
                break;
            }
        }
    }

    public static void main(String[] args) {
        Window window=new Window();
        Window window1=new Window();
        Window window2=new Window();
        window.setName("线程1");
        window.start();
        window1.start();
        window2.start();


    }

实现创建

卖票问题 

 可以直接用实例变量了  操作一个对象  所以数据可以100 开始但是有安全问题

package com.dto;

public class ThreadTest02 implements Runnable{

    private int ticket=100;

    @Override
    public void run() {
        while (true){
            if (ticket>0){
                System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                ticket--;
            }else {
                System.out.println("卖完了");
                break;
            }
        }
    }

    public static void main(String[] args) {
        //创建线程的类
        ThreadTest02 test02=new ThreadTest02();
        //创建线程
        Thread thread=new Thread(test02);
        Thread thread01=new Thread(test02);
        Thread thread02=new Thread(test02);
        thread.start();
        thread01.start();
        thread02.start();
        //线程里面有runnable 类型的target变量 当线程的run为null  使用了变量的run方法
    }
}


两种创建方式的对比


线程的生命周期

线程state

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

状态装换

状态改变之间可以书写代码   状态切换时会干什么   记住要点


线程安全问题 

上面使用的多线程  对某个数据 操作 会出现线程安全问题

  

 当线程出现睡眠是  变量可能被其他线程操作  最后出现 0  -1 

安全问题:一个线程对数据没执行完其他线程操作了数据

解决安全问题   :加锁  拿到数据加锁


同步代码块实现线程同步

最初两种方式:

  • 同步代码块(实现接口的方法)
  • 同步方法

同步代码块 

//同步监视器 俗称锁  :任何一个类的对象都可以
// 锁的要求:多个线程共用一把锁。
synchronized (同步监视器){
//需要被同步的代码
    //既是操作共享数据的代码
    //共享数据:多线程共享的变量
}

package com.dto;

public class ThreadTest02 implements Runnable{

    private int ticket=100;
//  同一对象
    Object object=new Object();
    @Override
    public void run() {
        while (true){

            synchronized (object){
                if (ticket>0){
                    System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                    ticket--;
                }else {
                    System.out.println("卖完了");
                    break;
                }
            }

        }
    }

    public static void main(String[] args) {
        //创建线程的类
        ThreadTest02 test02=new ThreadTest02();
        //创建线程
        Thread thread=new Thread(test02);
        Thread thread01=new Thread(test02);
        Thread thread02=new Thread(test02);
        thread.start();
        thread01.start();
        thread02.start();
        //线程里面有runnable 类型的target变量 当线程的run为null  使用了变量的run方法
    }
}

对象放在run里面的话  对象每次都创建一个就不是同一对象  也有问题

@Override
    public void run() {
        Object object=new Object();
        while (true){

            synchronized (object){
                if (ticket>0){
                    System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                    ticket--;
                }else {
                    System.out.println("卖完了");
                    break;
                }
            }

        }
    }

好处:

        解决安全问题

坏处:

        操作同步代码时  只能有一个线程操作 相当于单线程使用了。效率低


 

使用继承的同步代码块

使用继承时,因为都会创建Thread的接对象  里面的对象就不是唯一

package com.dto;
/*有问题的锁对象     每次创建线程的充当锁的不是一个对象*/
public class Window extends Thread{
    private  static int ticket=100;
    Object object =new Object();
    @Override
    public void run() {
        while (true){
            synchronized (object){
                if (ticket>0){
                System.out.println(getName()+"卖票了"+"票号为:"+ticket);
                ticket--;
            }else {
                System.out.println("卖完了");
                break;
            }
            }

        }
    }

    public static void main(String[] args) {
        Window window=new Window();
        Window window1=new Window();
        Window window2=new Window();
        window.setName("线程1");
        window.start();
        window1.start();
        window2.start();


    }
}


加static  使得只有一个  (注意操作的变量需要唯一  使用static修饰ticket)



this可以用在实现里面做锁监视器

在继承中每次的都是不一样的this

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

使用类.class可以成为同步监视器。类也是对象
得到一个class对象,使用类.class得到的对象是唯一的,因为类对象只加载一次



同步方法

 


将操作共享数据的方法做成同步方法、    返回值前面加synchronized。
实现中使用同步方法

@Override
    public void run() {
        while (true){
            synchronized (Thread.class){
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+"卖票了"+"票号为:"+ticket);
                ticket--;
            }else {
                System.out.println("卖完了");
                break;
            }
            }
        }
    }


提取操作数据的代码为一个方法,加价关键字,run调用
方法的同步监视器为this

package com.dto;

public class ThreadTest02 implements Runnable{

    private int ticket=100;
//  同一对象
    //Object object=new Object();
    @Override
    public void run() {
        Object object=new Object();
        while (true){
            extracted();
            if (ticket==0){
                break;
            }
        }
    }

    private synchronized void extracted() {
        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
            ticket--;
        }else {
            System.out.println("卖完了");
        }
    }

    public static void main(String[] args) {
        //创建线程的类
        ThreadTest02 test02=new ThreadTest02();
        //创建线程
        Thread thread=new Thread(test02);
        Thread thread01=new Thread(test02);
        Thread thread02=new Thread(test02);
        thread.start();
        thread01.start();
        thread02.start();
        //线程里面有runnable 类型的target变量 当线程的run为null  使用了变量的run方法
    }
}


继承中方法加synchronized  会出现问题,因为指定时当前 锁为  this  但是每次创建的线程this不是同一个。
需要加static 在synchronized前  监视器:this.class




双检锁实现线程安全的单例模式  懒汉式
 

 这里锁对象也是this  但是线程操作同一个对象时都是它  所以没有安全问题  但是等同于单线程

效率慢

package com.dto;

public class CLASS {
    public CLASS() {
    }
    //静态变量  保证只有一个惬意对象
    private static CLASS aClass=null;
    //同步方法
    public static synchronized CLASS get(){
        if (aClass==null){
            aClass=new CLASS();

        }
        return aClass;
    }

}

 效率高的双检锁  不用线程等待了  所有线程都可以去到第一个判断

package com.dto;

public class CLASS {
    public CLASS() {
    }
    //静态变量  保证只有一个惬意对象
    private static CLASS aClass=null;
    //同步方法
    public static  CLASS get(){
        if (aClass == null){
            synchronized(CLASS.class){
                if (aClass==null){
                    aClass=new CLASS();

                }
            }
        }
        return aClass;
    }

}

死锁

不同的的线程占用其他线程所需资源  等待对方释放资源。

死锁举例:【说一下什么是死锁   说完就录取你 /    录取我就告诉你】

下面会出现  但是效率比较低  可以加

package com.dto;

public class ThreadTest07 {
    public static void main(String[] args) {
        StringBuffer stringBuffer=new StringBuffer();
        StringBuffer stringBuffer1 =new StringBuffer();

        new Thread(){
            @Override
            public void run() {
                synchronized (stringBuffer){
                    stringBuffer.append("1");
                    stringBuffer1.append("2");
                    
                    synchronized (stringBuffer1){
                        stringBuffer.append(3);
                        stringBuffer1.append(4);
                        System.out.println(stringBuffer);
                        System.out.println(stringBuffer1);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (stringBuffer1){
                    stringBuffer.append("1");
                    stringBuffer1.append("2");
                    synchronized (stringBuffer){
                        stringBuffer.append(3);
                        stringBuffer1.append(4);
                        System.out.println(stringBuffer);
                        System.out.println(stringBuffer1);
                    }
                }
            }
        }).start();
    }
}

 不死亡  不关闭什么也不执行

 解决办法


Lock解决线程安全问题 jdk5.0

 

 

//  1、实例化
    ReentrantLock lock =new ReentrantLock();   参数  true  公平锁 flase不公平锁  不一定公平 还得等时间调度
//2、调用lock()方法 和操作资源的代码一起 写在try里面  为了在finally里面释放锁
lock.lock();
//3、解锁  写在finally    下面代码没有catch  是因为没有异常需要处理了。
lock.unlock();
package com.dto;

import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest02 implements Runnable{

    private int ticket=100;
//  1、实例化
    ReentrantLock lock =new ReentrantLock(true);
    @Override
    public void run() {
        Object object=new Object();
        while (true){
            try{
                //2、调用lock()方法
                lock.lock();
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                    ticket--;
                }else {
                    System.out.println("卖完了");
                    break;
                }
            }finally {
                //3、解锁
                lock.unlock();
            }
            //2、调用lock()方法
            lock.lock();
            if (ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                ticket--;
            }else {
                System.out.println("卖完了");
                break;
            }
        }
    }


    public static void main(String[] args) {
        //创建线程的类
        ThreadTest02 test02=new ThreadTest02();
        //创建线程
        Thread thread=new Thread(test02);
        Thread thread01=new Thread(test02);
        Thread thread02=new Thread(test02);
        thread.start();
        thread01.start();
        thread02.start();
        //线程里面有runnable 类型的target变量 当线程的run为null  使用了变量的run方法
    }
}

 

与 synchronized (someObject) 类似的,lock()方法,表示当前线程占用lock对象,一旦占用,其他线程就不能占用了。
与 synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行。


线程间的通信

循环打印100  一个线程打印一次

sleep  不释放锁  

wait 释放锁

线程1 进去了方法锁起来执行   执行完  wait  然后释放锁  线程2可以进来  执行notify  接触线程1的wait状态进入就绪状态   线程2执行。

实现创建线程。

package com.dto;

import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest02 implements Runnable{

    private int ticket=100;

    @Override
    public void run() {
        Object object=new Object();
        while (true){
            synchronized (this){
                //解除线程的wait状态
                notify();
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖票了"+"票号为:"+ticket);
                    ticket--;
                }else {
                    System.out.println("卖完了");
                    break;
                }
                try {
                    //使线程进入阻塞状态   释放锁  
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }


    public static void main(String[] args) {
        //创建线程的类
        ThreadTest02 test02=new ThreadTest02();
        //创建线程
        Thread thread=new Thread(test02);
        Thread thread01=new Thread(test02);
        Thread thread02=new Thread(test02);
        thread.start();
        thread01.start();
        thread02.start();
        //线程里面有runnable 类型的target变量 当线程的run为null  使用了变量的run方法
    }
}

 

 


 

调用三个方法的对象必须是   同步监视器(锁对象) 在java.lang.object

所以使用继承方式的时候选取锁对象很重要。。。。。。。。。。。。。。。

 

Lock实现通信 

(1条消息) 多线程 第八节 线程间通信之Lock_ellen艾琳的博客-CSDN博客 


 

Sleep 和wait的区别  

同:

        都可以使线程进入阻塞状态

        都需要捕获异常

异:

        方法声明位置不同  一个在Thread下  ,一个在Object下

        调用要求不同:sleep任何场景    wait同步代码块和同步方法中  调用对象必须是同步监视器

        释放锁: sleep不释放   wait释放锁


经典例题 生产者消费者问题

学会分析

 

package com.dto;

/**clerk 店员
 * 店员可以对生成者消费者做记账统计
 */
class Clerk {
    private int count=0; //数量
//同步生产方法
    public synchronized void produce()  {
        if (count<20){

            count++;
            System.out.println(Thread.currentThread().getName()+"开始生产第"+count+"产品");
        //加加之后告诉消费者可以接触阻塞进来了   
         notify();
        }else {
//>=20  不生产了
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
//   同上
    public synchronized void consume() {
        if (count>0){
            System.out.println(Thread.currentThread().getName()+"开始消费第"+count+"产品");
            count--;
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//生产者 线程
class Producer extends Thread{
    Clerk clerk;
    //加构造器
    public Producer(Clerk clerk){
        this.clerk=clerk;
    }
    @Override
    public void run() {
        while (true){
                clerk.produce();
        }
    }
}
// 消费者线程
class Consume extends Thread{
    Clerk clerk;
    //加构造器
    public Consume(Clerk clerk){
        this.clerk=clerk;
    }
    @Override
    public void run() {
        while (true){
            clerk.consume();
        }
    }
}
//主方法
public class ThreadTest08 {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Producer producer=new Producer(clerk);
        Consume consume = new Consume(clerk);
        producer.start();
        consume.start();
    }
}

创建多线程  :线程池

 

 实现callable接口

run不可以抛异常 能try catch  不能看到出现什么问题。   

package com.dto;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
// 1、实现
public class CallableTest implements Callable {
    //2、重写
    @Override
    public Object call() throws Exception {
        int sum=0;
        for (int i = 0; i < 10; i++) {
            sum+=i;
        }
        return sum;
    }

    public static void main(String[] args) {
        //  3、创捷实现接口的对象
        CallableTest callableTest = new CallableTest();
        //  4、创建得到返回值的对象FutureTask
        FutureTask futureTask=new FutureTask(callableTest);
        //  5、新建线程传入 FutureTask 对象  对象实现了runnable接口
        new Thread(futureTask).start();
        //  6、想得到返回值  需要用 futureTask.get
        try {
            Object o = futureTask.get();
            System.out.println("总和为"+o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

1、得到的返回值可以交给其他线程去操作

2、实际的返回值因为线程Thread没有方法  实际用的就是FutrueTask的方法  做一个中间代理

3、实现Callable可以加泛型  就是对线程返回值做规定


 

线程池  :

线程池的好处:

        避免频繁创建和销毁线程  消耗io资源

        便于线程管理   池大小   最大线程数 线程没有任务保存时间

 shutdown  关闭连接池

package com.dto;

import java.util.concurrent.*;

public class Test {
    public static void main(String[] args) {
        // 线程池创建  ExecutorService是接口  实际的作用是是实现类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //线程池作用类
        ThreadPoolExecutor service = (ThreadPoolExecutor) executorService;
        service.setCorePoolSize(2);
        //执行的参数还是我们自己定义线程
        executorService.execute(new Runnable() {
            @Override
            public void run() {

            }
        });
        try {
            executorService.submit(new CallableTest()).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

 


volatile关键字  

public class Test {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<10;j++)
                        test.increase();
                };
            }.start();
        }
        
        System.out.println(test.inc);
    }
}

结果可能  50  78  90   .。。。

没有保证原子性操作    执行完才结束  

线程1 拿到数据没做计算阻塞  线程2拿到了计算了返回  线程1在计算  内存只得到了线程1的结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值